" Test various aspects of the Vim script language.
" Most of this was formerly in test49.vim (developed by Servatius Brandt
" <Servatius.Brandt@fujitsu-siemens.com>)

source check.vim
source shared.vim
source script_util.vim

"-------------------------------------------------------------------------------
" Test environment							    {{{1
"-------------------------------------------------------------------------------

" Append a message to the "messages" file
func Xout(text)
    split messages
    $put =a:text
    wq
endfunc

com! -nargs=1	     Xout     call Xout(<args>)

" Create a new instance of Vim and run the commands in 'test' and then 'verify'
" The commands in 'test' are expected to store the test results in the Xtest.out
" file. If the test passes successfully, then Xtest.out should be empty.
func RunInNewVim(test, verify)
  let init =<< trim END
    set cpo-=C            " support line-continuation in sourced script
    source script_util.vim
    XpathINIT
    XloopINIT
  END
  let cleanup =<< trim END
    call writefile(v:errors, 'Xtest.out')
    qall
  END
  call writefile(init, 'Xtest.vim', 'D')
  call writefile(a:test, 'Xtest.vim', 'a')
  call writefile(a:verify, 'Xverify.vim', 'D')
  call writefile(cleanup, 'Xverify.vim', 'a')
  call RunVim([], [], "-S Xtest.vim -S Xverify.vim")
  call assert_equal([], readfile('Xtest.out'))
  call delete('Xtest.out')
endfunc

"-------------------------------------------------------------------------------
" Test 1:   :endwhile in function					    {{{1
"
"	    Detect if a broken loop is (incorrectly) reactivated by the
"	    :endwhile.  Use a :return to prevent an endless loop, and make
"	    this test first to get a meaningful result on an error before other
"	    tests will hang.
"-------------------------------------------------------------------------------

func T1_F()
    Xpath 'a'
    let first = 1
    while 1
	Xpath 'b'
	if first
	    Xpath 'c'
	    let first = 0
	    break
	else
	    Xpath 'd'
	    return
	endif
    endwhile
endfunc

func T1_G()
    Xpath 'h'
    let first = 1
    while 1
	Xpath 'i'
	if first
	    Xpath 'j'
	    let first = 0
	    break
	else
	    Xpath 'k'
	    return
	endif
	if 1	" unmatched :if
    endwhile
endfunc

func Test_endwhile_function()
  XpathINIT
  call T1_F()
  Xpath 'F'

  try
    call T1_G()
  catch
    " Catch missing :endif
    call assert_true(v:exception =~ 'E171:')
    Xpath 'x'
  endtry
  Xpath 'G'

  call assert_equal('abcFhijxG', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 2:   :endwhile in script						    {{{1
"
"	    Detect if a broken loop is (incorrectly) reactivated by the
"	    :endwhile.  Use a :finish to prevent an endless loop, and place
"	    this test before others that might hang to get a meaningful result
"	    on an error.
"
"	    This test executes the bodies of the functions T1_F and T1_G from
"	    the previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_endwhile_script()
  XpathINIT
  ExecAsScript T1_F
  Xpath 'F'
  call DeleteTheScript()

  try
    ExecAsScript T1_G
  catch
    " Catch missing :endif
    call assert_true(v:exception =~ 'E171:')
    Xpath 'x'
  endtry
  Xpath 'G'
  call DeleteTheScript()

  call assert_equal('abcFhijxG', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 3:   :if, :elseif, :while, :continue, :break			    {{{1
"-------------------------------------------------------------------------------

func Test_if_while()
    XpathINIT
    if 1
	Xpath 'a'
	let loops = 3
	while loops > -1	    " main loop: loops == 3, 2, 1 (which breaks)
	    if loops <= 0
		let break_err = 1
		let loops = -1
	    else
		Xpath 'b' . loops
	    endif
	    if (loops == 2)
		while loops == 2 " dummy loop
		    Xpath 'c' . loops
		    let loops = loops - 1
		    continue    " stop dummy loop
		    Xpath 'd' . loops
		endwhile
		continue	    " continue main loop
		Xpath 'e' . loops
	    elseif (loops == 1)
		let p = 1
		while p	    " dummy loop
		    Xpath 'f' . loops
		    let p = 0
		    break	    " break dummy loop
		    Xpath 'g' . loops
		endwhile
		Xpath 'h' . loops
		unlet p
		break	    " break main loop
		Xpath 'i' . loops
	    endif
	    if (loops > 0)
		Xpath 'j' . loops
	    endif
	    while loops == 3    " dummy loop
		let loops = loops - 1
	    endwhile	    " end dummy loop
	endwhile		    " end main loop
	Xpath 'k'
    else
	Xpath 'l'
    endif
    Xpath 'm'
    if exists("break_err")
	Xpath 'm'
	unlet break_err
    endif

    unlet loops

    call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath)
endfunc

" Check double quote after skipped "elseif" does not give error E15
func Test_skipped_elseif()
  if "foo" ==? "foo"
      let result = "first"
  elseif "foo" ==? "foo"
      let result = "second"
  endif
  call assert_equal('first', result)
endfunc

"-------------------------------------------------------------------------------
" Test 4:   :return							    {{{1
"-------------------------------------------------------------------------------

func T4_F()
    if 1
	Xpath 'a'
	let loops = 3
	while loops > 0				"    3:  2:     1:
	    Xpath 'b' . loops
	    if (loops == 2)
		Xpath 'c' . loops
		return
		Xpath 'd' . loops
	    endif
	    Xpath 'e' . loops
	    let loops = loops - 1
	endwhile
	Xpath 'f'
    else
	Xpath 'g'
    endif
endfunc

func Test_return()
    XpathINIT
    call T4_F()
    Xpath '4'

    call assert_equal('ab3e3b2c24', g:Xpath)
endfunc


"-------------------------------------------------------------------------------
" Test 5:   :finish							    {{{1
"
"	    This test executes the body of the function T4_F from the previous
"	    test as a script file (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_finish()
    XpathINIT
    ExecAsScript T4_F
    Xpath '5'
    call DeleteTheScript()

    call assert_equal('ab3e3b2c25', g:Xpath)
endfunc



"-------------------------------------------------------------------------------
" Test 6:   Defining functions in :while loops				    {{{1
"
"	     Functions can be defined inside other functions.  An inner function
"	     gets defined when the outer function is executed.  Functions may
"	     also be defined inside while loops.  Expressions in braces for
"	     defining the function name are allowed.
"
"	     The functions are defined when sourcing the script, only the
"	     resulting path is checked in the test function.
"-------------------------------------------------------------------------------

XpathINIT

" The command CALL collects the argument of all its invocations in "calls"
" when used from a function (that is, when the global variable "calls" needs
" the "g:" prefix).  This is to check that the function code is skipped when
" the function is defined.  For inner functions, do so only if the outer
" function is not being executed.
"
let calls = ""
com! -nargs=1 CALL
	    \ if !exists("calls") && !exists("outer") |
	    \ let g:calls = g:calls . <args> |
	    \ endif

let i = 0
while i < 3
    let i = i + 1
    if i == 1
	Xpath 'a'
	function! F1(arg)
	    CALL a:arg
	    let outer = 1

	    let j = 0
	    while j < 1
		Xpath 'b'
		let j = j + 1
		function! G1(arg)
		    CALL a:arg
		endfunction
		Xpath 'c'
	    endwhile
	endfunction
	Xpath 'd'

	continue
    endif

    Xpath 'e' . i
    function! F{i}(i, arg)
	CALL a:arg
	let outer = 1

	if a:i == 3
	    Xpath 'f'
	endif
	let k = 0
	while k < 3
	    Xpath 'g' . k
	    let k = k + 1
	    function! G{a:i}{k}(arg)
		CALL a:arg
	    endfunction
	    Xpath 'h' . k
	endwhile
    endfunction
    Xpath 'i'

endwhile

if exists("*G1")
    Xpath 'j'
endif
if exists("*F1")
    call F1("F1")
    if exists("*G1")
       call G1("G1")
    endif
endif

if exists("G21") || exists("G22") || exists("G23")
    Xpath 'k'
endif
if exists("*F2")
    call F2(2, "F2")
    if exists("*G21")
       call G21("G21")
    endif
    if exists("*G22")
       call G22("G22")
    endif
    if exists("*G23")
       call G23("G23")
    endif
endif

if exists("G31") || exists("G32") || exists("G33")
    Xpath 'l'
endif
if exists("*F3")
    call F3(3, "F3")
    if exists("*G31")
       call G31("G31")
    endif
    if exists("*G32")
       call G32("G32")
    endif
    if exists("*G33")
       call G33("G33")
    endif
endif

Xpath 'm'

let g:test6_result = g:Xpath
let g:test6_calls = calls

unlet calls
delfunction F1
delfunction G1
delfunction F2
delfunction G21
delfunction G22
delfunction G23
delfunction G31
delfunction G32
delfunction G33

func Test_defining_functions()
    call assert_equal('ade2ie3ibcg0h1g1h2g2h3fg0h1g1h2g2h3m', g:test6_result)
    call assert_equal('F1G1F2G21G22G23F3G31G32G33', g:test6_calls)
endfunc

"-------------------------------------------------------------------------------
" Test 7:   Continuing on errors outside functions			    {{{1
"
"	    On an error outside a function, the script processing continues
"	    at the line following the outermost :endif or :endwhile.  When not
"	    inside an :if or :while, the script processing continues at the next
"	    line.
"-------------------------------------------------------------------------------

XpathINIT

if 1
    Xpath 'a'
    while 1
	Xpath 'b'
	asdf
	Xpath 'c'
	break
    endwhile | Xpath 'd'
    Xpath 'e'
endif | Xpath 'f'
Xpath 'g'

while 1
    Xpath 'h'
    if 1
	Xpath 'i'
	asdf
	Xpath 'j'
    endif | Xpath 'k'
    Xpath 'l'
    break
endwhile | Xpath 'm'
Xpath 'n'

asdf
Xpath 'o'

asdf | Xpath 'p'
Xpath 'q'

let g:test7_result = g:Xpath

func Test_error_in_script()
    call assert_equal('abghinoq', g:test7_result)
endfunc

"-------------------------------------------------------------------------------
" Test 8:   Aborting and continuing on errors inside functions		    {{{1
"
"	    On an error inside a function without the "abort" attribute, the
"	    script processing continues at the next line (unless the error was
"	    in a :return command).  On an error inside a function with the
"	    "abort" attribute, the function is aborted and the script processing
"	    continues after the function call; the value -1 is returned then.
"-------------------------------------------------------------------------------

XpathINIT

func T8_F()
    if 1
	Xpath 'a'
	while 1
	    Xpath 'b'
	    asdf
	    Xpath 'c'
	    asdf | Xpath 'd'
	    Xpath 'e'
	    break
	endwhile
	Xpath 'f'
    endif | Xpath 'g'
    Xpath 'h'

    while 1
	Xpath 'i'
	if 1
	    Xpath 'j'
	    asdf
	    Xpath 'k'
	    asdf | Xpath 'l'
	    Xpath 'm'
	endif
	Xpath 'n'
	break
    endwhile | Xpath 'o'
    Xpath 'p'

    return novar		" returns (default return value 0)
    Xpath 'q'
    return 1			" not reached
endfunc

func T8_G() abort
    if 1
	Xpath 'r'
	while 1
	    Xpath 's'
	    asdf		" returns -1
	    Xpath 't'
	    break
	endwhile
	Xpath 'v'
    endif | Xpath 'w'
    Xpath 'x'

    return -4			" not reached
endfunc

func T8_H() abort
    while 1
	Xpath 'A'
	if 1
	    Xpath 'B'
	    asdf		" returns -1
	    Xpath 'C'
	endif
	Xpath 'D'
	break
    endwhile | Xpath 'E'
    Xpath 'F'

    return -4			" not reached
endfunc

" Aborted functions (T8_G and T8_H) return -1.
let g:test8_sum = (T8_F() + 1) - 4 * T8_G() - 8 * T8_H()
Xpath 'X'
let g:test8_result = g:Xpath

func Test_error_in_function()
    call assert_equal(13, g:test8_sum)
    call assert_equal('abcefghijkmnoprsABX', g:test8_result)

    delfunction T8_F
    delfunction T8_G
    delfunction T8_H
endfunc


"-------------------------------------------------------------------------------
" Test 9:   Continuing after aborted functions				    {{{1
"
"	    When a function with the "abort" attribute is aborted due to an
"	    error, the next function back in the call hierarchy without an
"	    "abort" attribute continues; the value -1 is returned then.
"-------------------------------------------------------------------------------

XpathINIT

func F() abort
    Xpath 'a'
    let result = G()	" not aborted
    Xpath 'b'
    if result != 2
	Xpath 'c'
    endif
    return 1
endfunc

func G()		" no abort attribute
    Xpath 'd'
    if H() != -1	" aborted
	Xpath 'e'
    endif
    Xpath 'f'
    return 2
endfunc

func H() abort
    Xpath 'g'
    call I()		" aborted
    Xpath 'h'
    return 4
endfunc

func I() abort
    Xpath 'i'
    asdf		" error
    Xpath 'j'
    return 8
endfunc

if F() != 1
    Xpath 'k'
endif

let g:test9_result = g:Xpath

delfunction F
delfunction G
delfunction H
delfunction I

func Test_func_abort()
    call assert_equal('adgifb', g:test9_result)
endfunc


"-------------------------------------------------------------------------------
" Test 10:  :if, :elseif, :while argument parsing			    {{{1
"
"	    A '"' or '|' in an argument expression must not be mixed up with
"	    a comment or a next command after a bar.  Parsing errors should
"	    be recognized.
"-------------------------------------------------------------------------------

XpathINIT

func MSG(enr, emsg)
    let english = v:lang == "C" || v:lang =~ '^[Ee]n'
    if a:enr == ""
	Xout "TODO: Add message number for:" a:emsg
	let v:errmsg = ":" . v:errmsg
    endif
    let match = 1
    if v:errmsg !~ '^'.a:enr.':' || (english && v:errmsg !~ a:emsg)
	let match = 0
	if v:errmsg == ""
	    Xout "Message missing."
	else
	    let v:errmsg = v:errmsg->escape('"')
	    Xout "Unexpected message:" v:errmsg
	endif
    endif
    return match
endfunc

if 1 || strlen("\"") | Xpath 'a'
    Xpath 'b'
endif
Xpath 'c'

if 0
elseif 1 || strlen("\"") | Xpath 'd'
    Xpath 'e'
endif
Xpath 'f'

while 1 || strlen("\"") | Xpath 'g'
    Xpath 'h'
    break
endwhile
Xpath 'i'

let v:errmsg = ""
if 1 ||| strlen("\"") | Xpath 'j'
    Xpath 'k'
endif
Xpath 'l'
if !MSG('E15', "Invalid expression")
    Xpath 'm'
endif

let v:errmsg = ""
if 0
elseif 1 ||| strlen("\"") | Xpath 'n'
    Xpath 'o'
endif
Xpath 'p'
if !MSG('E15', "Invalid expression")
    Xpath 'q'
endif

let v:errmsg = ""
while 1 ||| strlen("\"") | Xpath 'r'
    Xpath 's'
    break
endwhile
Xpath 't'
if !MSG('E15', "Invalid expression")
    Xpath 'u'
endif

let g:test10_result = g:Xpath
delfunction MSG

func Test_expr_parsing()
    call assert_equal('abcdefghilpt', g:test10_result)
endfunc


"-------------------------------------------------------------------------------
" Test 11:  :if, :elseif, :while argument evaluation after abort	    {{{1
"
"	    When code is skipped over due to an error, the boolean argument to
"	    an :if, :elseif, or :while must not be evaluated.
"-------------------------------------------------------------------------------

XpathINIT

let calls = 0

func P(num)
    let g:calls = g:calls + a:num   " side effect on call
    return 0
endfunc

if 1
    Xpath 'a'
    asdf		" error
    Xpath 'b'
    if P(1)		" should not be called
	Xpath 'c'
    elseif !P(2)	" should not be called
	Xpath 'd'
    else
	Xpath 'e'
    endif
    Xpath 'f'
    while P(4)		" should not be called
	Xpath 'g'
    endwhile
    Xpath 'h'
endif
Xpath 'x'

let g:test11_calls = calls
let g:test11_result = g:Xpath

unlet calls
delfunction P

func Test_arg_abort()
    call assert_equal(0, g:test11_calls)
    call assert_equal('ax', g:test11_result)
endfunc


"-------------------------------------------------------------------------------
" Test 12:  Expressions in braces in skipped code			    {{{1
"
"	    In code skipped over due to an error or inactive conditional,
"	    an expression in braces as part of a variable or function name
"	    should not be evaluated.
"-------------------------------------------------------------------------------

XpathINIT

func NULL()
    Xpath 'a'
    return 0
endfunc

func ZERO()
    Xpath 'b'
    return 0
endfunc

func! F0()
    Xpath 'c'
endfunc

func! F1(arg)
    Xpath 'e'
endfunc

let V0 = 1

Xpath 'f'
echo 0 ? F{NULL() + V{ZERO()}}() : 1

Xpath 'g'
if 0
    Xpath 'h'
    call F{NULL() + V{ZERO()}}()
endif

Xpath 'i'
if 1
    asdf		" error
    Xpath 'j'
    call F1(F{NULL() + V{ZERO()}}())
endif

Xpath 'k'
if 1
    asdf		" error
    Xpath 'l'
    call F{NULL() + V{ZERO()}}()
endif

let g:test12_result = g:Xpath

func Test_braces_skipped()
    call assert_equal('fgik', g:test12_result)
endfunc


"-------------------------------------------------------------------------------
" Test 13:  Failure in argument evaluation for :while			    {{{1
"
"	    A failure in the expression evaluation for the condition of a :while
"	    causes the whole :while loop until the matching :endwhile being
"	    ignored.  Continuation is at the next following line.
"-------------------------------------------------------------------------------

XpathINIT

Xpath 'a'
while asdf
    Xpath 'b'
    while 1
	Xpath 'c'
	break
    endwhile
    Xpath 'd'
    break
endwhile
Xpath 'e'

while asdf | Xpath 'f' | endwhile | Xpath 'g'
Xpath 'h'
let g:test13_result = g:Xpath

func Test_while_fail()
    call assert_equal('aeh', g:test13_result)
endfunc


"-------------------------------------------------------------------------------
" Test 14:  Failure in argument evaluation for :if			    {{{1
"
"	    A failure in the expression evaluation for the condition of an :if
"	    does not cause the corresponding :else or :endif being matched to
"	    a previous :if/:elseif.  Neither of both branches of the failed :if
"	    are executed.
"-------------------------------------------------------------------------------

XpathINIT

function! F()
    Xpath 'a'
    let x = 0
    if x		" false
	Xpath 'b'
    elseif !x		" always true
	Xpath 'c'
	let x = 1
	if g:boolvar	" possibly undefined
	    Xpath 'd'
	else
	    Xpath 'e'
	endif
	Xpath 'f'
    elseif x		" never executed
	Xpath 'g'
    endif
    Xpath 'h'
endfunction

let boolvar = 1
call F()
Xpath '-'

unlet boolvar
call F()
let g:test14_result = g:Xpath

delfunction F

func Test_if_fail()
    call assert_equal('acdfh-acfh', g:test14_result)
endfunc


"-------------------------------------------------------------------------------
" Test 15:  Failure in argument evaluation for :if (bar)		    {{{1
"
"	    Like previous test, except that the failing :if ... | ... | :endif
"	    is in a single line.
"-------------------------------------------------------------------------------

XpathINIT

function! F()
    Xpath 'a'
    let x = 0
    if x		" false
	Xpath 'b'
    elseif !x		" always true
	Xpath 'c'
	let x = 1
	if g:boolvar | Xpath 'd' | else | Xpath 'e' | endif
	Xpath 'f'
    elseif x		" never executed
	Xpath 'g'
    endif
    Xpath 'h'
endfunction

let boolvar = 1
call F()
Xpath '-'

unlet boolvar
call F()
let g:test15_result = g:Xpath

delfunction F

func Test_if_bar_fail()
    call assert_equal('acdfh-acfh', g:test15_result)
endfunc

"-------------------------------------------------------------------------------
" Test 16:  Double :else or :elseif after :else				    {{{1
"
"	    Multiple :elses or an :elseif after an :else are forbidden.
"-------------------------------------------------------------------------------

func T16_F() abort
  if 0
    Xpath 'a'
  else
    Xpath 'b'
  else		" aborts function
    Xpath 'c'
  endif
  Xpath 'd'
endfunc

func T16_G() abort
  if 0
    Xpath 'a'
  else
    Xpath 'b'
  elseif 1		" aborts function
    Xpath 'c'
  else
    Xpath 'd'
  endif
  Xpath 'e'
endfunc

func T16_H() abort
  if 0
    Xpath 'a'
  elseif 0
    Xpath 'b'
  else
    Xpath 'c'
  else		" aborts function
    Xpath 'd'
  endif
  Xpath 'e'
endfunc

func T16_I() abort
  if 0
    Xpath 'a'
  elseif 0
    Xpath 'b'
  else
    Xpath 'c'
  elseif 1		" aborts function
    Xpath 'd'
  else
    Xpath 'e'
  endif
  Xpath 'f'
endfunc

func Test_Multi_Else()
  XpathINIT
  try
    call T16_F()
  catch /E583:/
    Xpath 'e'
  endtry
  call assert_equal('be', g:Xpath)

  XpathINIT
  try
    call T16_G()
  catch /E584:/
    Xpath 'f'
  endtry
  call assert_equal('bf', g:Xpath)

  XpathINIT
  try
    call T16_H()
  catch /E583:/
    Xpath 'f'
  endtry
  call assert_equal('cf', g:Xpath)

  XpathINIT
  try
    call T16_I()
  catch /E584:/
    Xpath 'g'
  endtry
  call assert_equal('cg', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 17:  Nesting of unmatched :if or :endif inside a :while		    {{{1
"
"	    The :while/:endwhile takes precedence in nesting over an unclosed
"	    :if or an unopened :endif.
"-------------------------------------------------------------------------------

" While loops inside a function are continued on error.
func T17_F()
  let loops = 3
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if (loops == 1)
      Xpath 'b' . loops
      continue
    elseif (loops == 0)
      Xpath 'c' . loops
      break
    elseif 1
      Xpath 'd' . loops
    " endif missing!
  endwhile	" :endwhile after :if 1
  Xpath 'e'
endfunc

func T17_G()
  let loops = 2
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if 0
      Xpath 'b' . loops
    " endif missing
  endwhile	" :endwhile after :if 0
endfunc

func T17_H()
  let loops = 2
  while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    " if missing!
    endif	" :endif without :if in while
    Xpath 'b' . loops
  endwhile
endfunc

" Error continuation outside a function is at the outermost :endwhile or :endif.
XpathINIT
let v:errmsg = ''
let loops = 2
while loops > 0
    let loops -= 1
    Xpath 'a' . loops
    if 0
	Xpath 'b' . loops
    " endif missing! Following :endwhile fails.
endwhile | Xpath 'c'
Xpath 'd'
call assert_match('E171:', v:errmsg)
call assert_equal('a1d', g:Xpath)

func Test_unmatched_if_in_while()
  XpathINIT
  call assert_fails('call T17_F()', 'E171:')
  call assert_equal('a2d2a1b1a0c0e', g:Xpath)

  XpathINIT
  call assert_fails('call T17_G()', 'E171:')
  call assert_equal('a1a0', g:Xpath)

  XpathINIT
  call assert_fails('call T17_H()', 'E580:')
  call assert_equal('a1b1a0b0', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 18:  Interrupt (Ctrl-C pressed)					    {{{1
"
"	    On an interrupt, the script processing is terminated immediately.
"-------------------------------------------------------------------------------

func Test_interrupt_while_if()
  let test =<< trim [CODE]
    try
      if 1
        Xpath 'a'
        while 1
          Xpath 'b'
          if 1
            Xpath 'c'
            call interrupt()
            call assert_report('should not get here')
            break
            finish
          endif | call assert_report('should not get here')
          call assert_report('should not get here')
        endwhile | call assert_report('should not get here')
        call assert_report('should not get here')
      endif | call assert_report('should not get here')
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'd'
    endtry | Xpath 'e'
    Xpath 'f'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdef', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_try()
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'b'
    endtry | Xpath 'c'
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_func_while_if()
  let test =<< trim [CODE]
    func F()
      if 1
        Xpath 'a'
        while 1
          Xpath 'b'
          if 1
            Xpath 'c'
            call interrupt()
            call assert_report('should not get here')
            break
            return
          endif | call assert_report('should not get here')
          call assert_report('should not get here')
        endwhile | call assert_report('should not get here')
        call assert_report('should not get here')
      endif | call assert_report('should not get here')
      call assert_report('should not get here')
    endfunc

    Xpath 'd'
    try
      call F() | call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'e'
    endtry | Xpath 'f'
    Xpath 'g'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('dabcefg', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_interrupt_func_try()
  let test =<< trim [CODE]
    func G()
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
      call assert_report('should not get here')
    endfunc

    Xpath 'b'
    try
      call G() | call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'c'
    endtry | Xpath 'd'
    Xpath 'e'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bacde', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 19:  Aborting on errors inside :try/:endtry			    {{{1
"
"	    An error in a command dynamically enclosed in a :try/:endtry region
"	    aborts script processing immediately.  It does not matter whether
"	    the failing command is outside or inside a function and whether a
"	    function has an "abort" attribute.
"-------------------------------------------------------------------------------

func Test_try_error_abort_1()
  let test =<< trim [CODE]
    func F() abort
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'b'
      call F()
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_2()
  let test =<< trim [CODE]
    func G()
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'b'
      call G()
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_3()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      asdf
      call assert_report('should not get here')
    endtry | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_4()
  let test =<< trim [CODE]
    if 1
      try
        Xpath 'a'
        asdf
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
    endif | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_5()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        asdf
        call assert_report('should not get here')
      endtry | call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_try_error_abort_6()
  let test =<< trim [CODE]
    let p = 1
    Xpath 'a'
    while p
      Xpath 'b'
      let p = 0
      try
        Xpath 'c'
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 20:  Aborting on errors after :try/:endtry			    {{{1
"
"	    When an error occurs after the last active :try/:endtry region has
"	    been left, termination behavior is as if no :try/:endtry has been
"	    seen.
"-------------------------------------------------------------------------------

func Test_error_after_try_1()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      Xpath 'a'
      try
        Xpath 'b'
      endtry
      asdf
      call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_2()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
        break
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'b'
    asdf
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_3()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
        break
        call assert_report('should not get here')
      finally
        Xpath 'b'
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_4()
  let test =<< trim [CODE]
    while 1
      try
        Xpath 'a'
      finally
        Xpath 'b'
        break
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_5()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        continue
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'b'
    asdf
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_6()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
        continue
        call assert_report('should not get here')
      finally
        Xpath 'b'
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_error_after_try_7()
  let test =<< trim [CODE]
    let p = 1
    while p
      let p = 0
      try
        Xpath 'a'
      finally
        Xpath 'b'
        continue
        call assert_report('should not get here')
      endtry
    endwhile
    Xpath 'c'
    asdf
    Xpath 'd'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcd', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 21:  :finally for :try after :continue/:break/:return/:finish	    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :continue,
"	    :break, :return, or :finish, its :finally clause should not be
"	    executed.
"-------------------------------------------------------------------------------

func Test_finally_after_loop_ctrl_statement()
  let test =<< trim [CODE]
    func F()
      let loops = 2
      while loops > 0
        XloopNEXT
        let loops = loops - 1
        try
          if loops == 1
            Xloop 'a'
            continue
            call assert_report('should not get here')
          elseif loops == 0
            Xloop 'b'
            break
            call assert_report('should not get here')
          endif

          try		" inactive
            call assert_report('should not get here')
          finally
            call assert_report('should not get here')
          endtry
        finally
          Xloop 'c'
        endtry
        call assert_report('should not get here')
      endwhile

      try
        Xpath 'd'
        return
        call assert_report('should not get here')
        try		    " inactive
          call assert_report('should not get here')
        finally
          call assert_report('should not get here')
        endtry
      finally
        Xpath 'e'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'f'
      call F()
      Xpath 'g'
      finish
      call assert_report('should not get here')
      try		" inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    finally
      Xpath 'h'
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('fa2c2b3c3degh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 22:  :finally for a :try after an error/interrupt/:throw		    {{{1
"
"	    If a :try conditional stays inactive due to a preceding error or
"	    interrupt or :throw, its :finally clause should not be executed.
"-------------------------------------------------------------------------------

func Test_finally_after_error_in_func()
  let test =<< trim [CODE]
    func Error()
      try
        Xpath 'b'
        asdf    " aborting error, triggering error exception
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    endfunc

    Xpath 'a'
    call Error()
    call assert_report('should not get here')

    if 1	" not active due to error
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to error
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_interrupt()
  let test =<< trim [CODE]
    func Interrupt()
      try
        Xpath 'a'
        call interrupt()            " triggering interrupt exception
        call assert_report('should not get here')
      endtry
    endfunc

    Xpath 'b'
    try
      call Interrupt()
    catch /^Vim:Interrupt$/
      Xpath 'c'
      finish
    endtry
    call assert_report('should not get here')

    if 1	" not active due to interrupt
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to interrupt
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bac', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_throw()
  let test =<< trim [CODE]
    func Throw()
      Xpath 'a'
      throw 'xyz'
    endfunc

    Xpath 'b'
    call Throw()
    call assert_report('should not get here')

    if 1	" not active due to :throw
      try	" not active since :if inactive
        call assert_report('should not get here')
      finally
        call assert_report('should not get here')
      endtry
    endif

    try		" not active due to :throw
      call assert_report('should not get here')
    finally
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ba', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 23:  :catch clauses for a :try after a :throw			    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :throw,
"	    none of its :catch clauses should be executed.
"-------------------------------------------------------------------------------

func Test_catch_after_throw()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "xyz"
      call assert_report('should not get here')

      if 1	" not active due to :throw
        try	" not active since :if inactive
          call assert_report('should not get here')
        catch /xyz/
          call assert_report('should not get here')
        endtry
      endif
    catch /xyz/
      Xpath 'b'
    endtry

    Xpath 'c'
    throw "abc"
    call assert_report('should not get here')

    try		" not active due to :throw
      call assert_report('should not get here')
    catch /abc/
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 24:  :endtry for a :try after a :throw				    {{{1
"
"	    If a :try conditional stays inactive due to a preceding :throw,
"	    its :endtry should not rethrow the exception to the next surrounding
"	    active :try conditional.
"-------------------------------------------------------------------------------

func Test_endtry_after_throw()
  let test =<< trim [CODE]
    try			" try 1
      try		" try 2
        Xpath 'a'
        throw "xyz"	" makes try 2 inactive
        call assert_report('should not get here')

        try		" try 3
          call assert_report('should not get here')
        endtry	" no rethrow to try 1
      catch /xyz/	" should catch although try 2 inactive
        Xpath 'b'
      endtry
    catch /xyz/		" try 1 active, but exception already caught
      call assert_report('should not get here')
    endtry
    Xpath 'c'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 27:  Executing :finally clauses after :return			    {{{1
"
"	    For a :return command dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the called function is ended.
"-------------------------------------------------------------------------------

func T27_F()
  try
    Xpath 'a'
    try
      Xpath 'b'
      return
      call assert_report('should not get here')
    finally
      Xpath 'c'
    endtry
    Xpath 'd'
  finally
    Xpath 'e'
  endtry
  call assert_report('should not get here')
endfunc

func T27_G()
  try
    Xpath 'f'
    return
    call assert_report('should not get here')
  finally
    Xpath 'g'
    call T27_F()
    Xpath 'h'
  endtry
  call assert_report('should not get here')
endfunc

func T27_H()
  try
    Xpath 'i'
    call T27_G()
    Xpath 'j'
  finally
    Xpath 'k'
    return
    call assert_report('should not get here')
  endtry
  call assert_report('should not get here')
endfunction

func Test_finally_after_return()
  XpathINIT
  try
      Xpath 'l'
      call T27_H()
      Xpath 'm'
  finally
      Xpath 'n'
  endtry
  call assert_equal('lifgabcehjkmn', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 28:  Executing :finally clauses after :finish			    {{{1
"
"	    For a :finish command dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the sourced file is finished.
"
"	    This test executes the bodies of the functions F, G, and H from the
"	    previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func Test_finally_after_finish()
  XpathINIT

  let scriptF = MakeScript("T27_F")
  let scriptG = MakeScript("T27_G", scriptF)
  let scriptH = MakeScript("T27_H", scriptG)

  try
    Xpath 'A'
    exec "source" scriptH
    Xpath 'B'
  finally
    Xpath 'C'
  endtry
  Xpath 'D'
  call assert_equal('AifgabcehjkBCD', g:Xpath)
  call delete(scriptF)
  call delete(scriptG)
  call delete(scriptH)
endfunc

"-------------------------------------------------------------------------------
" Test 29:  Executing :finally clauses on errors			    {{{1
"
"	    After an error in a command dynamically enclosed in a :try/:endtry
"	    region, :finally clauses are executed and the script processing is
"	    terminated.
"-------------------------------------------------------------------------------

func Test_finally_after_error_1()
  let test =<< trim [CODE]
    func F()
      while 1
        try
          Xpath 'a'
          while 1
            try
              Xpath 'b'
              asdf	    " error
              call assert_report('should not get here')
            finally
              Xpath 'c'
            endtry | call assert_report('should not get here')
            call assert_report('should not get here')
            break
          endwhile
          call assert_report('should not get here')
        finally
          Xpath 'd'
        endtry | call assert_report('should not get here')
        call assert_report('should not get here')
        break
      endwhile
      call assert_report('should not get here')
    endfunc

    while 1
      try
        Xpath 'e'
        while 1
          call F()
          call assert_report('should not get here')
          break
        endwhile  | call assert_report('should not get here')
        call assert_report('should not get here')
      finally
        Xpath 'f'
      endtry | call assert_report('should not get here')
    endwhile | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('eabcdf', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_finally_after_error_2()
  let test =<< trim [CODE]
    func G() abort
      if 1
        try
          Xpath 'a'
          asdf	    " error
          call assert_report('should not get here')
        finally
          Xpath 'b'
        endtry | Xpath 'c'
      endif | Xpath 'd'
      call assert_report('should not get here')
    endfunc

    if 1
      try
        Xpath 'e'
        call G()
        call assert_report('should not get here')
      finally
        Xpath 'f'
      endtry | call assert_report('should not get here')
    endif | call assert_report('should not get here')
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('eabf', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 30:  Executing :finally clauses on interrupt			    {{{1
"
"	    After an interrupt in a command dynamically enclosed in
"	    a :try/:endtry region, :finally clauses are executed and the
"	    script processing is terminated.
"-------------------------------------------------------------------------------

func Test_finally_on_interrupt()
  let test =<< trim [CODE]
    func F()
      try
        Xloop 'a'
        call interrupt()
        call assert_report('should not get here')
      finally
        Xloop 'b'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      try
        Xpath 'c'
        try
          Xpath 'd'
          call interrupt()
          call assert_report('should not get here')
        finally
          Xpath 'e'
          try
            Xpath 'f'
            try
              Xpath 'g'
            finally
              Xpath 'h'
              try
                Xpath 'i'
                call interrupt()
                call assert_report('should not get here')
              endtry
              call assert_report('should not get here')
            endtry
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      finally
        Xpath 'j'
        try
          Xpath 'k'
          call F()
          call assert_report('should not get here')
        finally
          Xpath 'l'
          try
            Xpath 'm'
            XloopNEXT
            ExecAsScript F
            call assert_report('should not get here')
          finally
            Xpath 'n'
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'o'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('cdefghijka1b1lma2b2no', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 31:  Executing :finally clauses after :throw			    {{{1
"
"	    After a :throw dynamically enclosed in a :try/:endtry region,
"	    :finally clauses are executed and the script processing is
"	    terminated.
"-------------------------------------------------------------------------------

func Test_finally_after_throw_2()
  let test =<< trim [CODE]
    func F()
      try
        Xloop 'a'
        throw "exception"
        call assert_report('should not get here')
      finally
        Xloop 'b'
      endtry
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'c'
      try
        Xpath 'd'
        throw "exception"
        call assert_report('should not get here')
      finally
        Xpath 'e'
        try
          Xpath 'f'
          try
            Xpath 'g'
          finally
            Xpath 'h'
            try
              Xpath 'i'
              throw "exception"
              call assert_report('should not get here')
            endtry
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    finally
      Xpath 'j'
      try
        Xpath 'k'
        call F()
        call assert_report('should not get here')
      finally
        Xpath 'l'
        try
          Xpath 'm'
          XloopNEXT
          ExecAsScript F
          call assert_report('should not get here')
        finally
          Xpath 'n'
        endtry
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('cdefghijka1b1lma2b2n', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 34:  :finally reason discarded by :continue			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :continue in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_after_continue()
  let test =<< trim [CODE]
    func C(jump)
      XloopNEXT
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            continue	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
          call assert_report('should not get here')
        elseif loop == 2
          Xloop 'a'
        endif
      endwhile
    endfunc

    call C("continue")
    Xpath 'b'
    call C("break")
    Xpath 'c'
    call C("return")
    Xpath 'd'
    let g:jump = "finish"
    ExecAsScript C
    unlet g:jump
    Xpath 'e'
    try
      call C("error")
      Xpath 'f'
    finally
      Xpath 'g'
      try
        call C("interrupt")
        Xpath 'h'
      finally
        Xpath 'i'
        call C("throw")
        Xpath 'j'
      endtry
    endtry
    Xpath 'k'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 35:  :finally reason discarded by :break				    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :break in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_break()
  let test =<< trim [CODE]
    func B(jump)
      XloopNEXT
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            break	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      Xloop 'a'
    endfunc

    call B("continue")
    Xpath 'b'
    call B("break")
    Xpath 'c'
    call B("return")
    Xpath 'd'
    let g:jump = "finish"
    ExecAsScript B
    unlet g:jump
    Xpath 'e'
    try
      call B("error")
      Xpath 'f'
    finally
      Xpath 'g'
      try
        call B("interrupt")
        Xpath 'h'
      finally
        Xpath 'i'
        call B("throw")
        Xpath 'j'
      endtry
    endtry
    Xpath 'k'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a2ba3ca4da5ea6fga7hia8jk', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 36:  :finally reason discarded by :return			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :return in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_return()
  let test =<< trim [CODE]
    func R(jump, retval) abort
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            return a:retval	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    let sum =  -R("continue", -8)
    Xpath 'a'
    let sum = sum - R("break", -16)
    Xpath 'b'
    let sum = sum - R("return", -32)
    Xpath 'c'
    try
      let sum = sum - R("error", -64)
      Xpath 'd'
    finally
      Xpath 'e'
      try
        let sum = sum - R("interrupt", -128)
        Xpath 'f'
      finally
        Xpath 'g'
        let sum = sum - R("throw", -256)
        Xpath 'h'
      endtry
    endtry
    Xpath 'i'

    let expected = 8 + 16 + 32 + 64 + 128 + 256
    call assert_equal(sum, expected)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 37:  :finally reason discarded by :finish			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :finish in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_finish()
  let test =<< trim [CODE]
    func F(jump)	" not executed as function, transformed to a script
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "finish"
              finish
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            finish	" discards jump that caused the :finally
            call assert_report('should not get here')
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    let scriptF = MakeScript("F")
    delfunction F

    let g:jump = "continue"
    exec "source" scriptF
    Xpath 'a'
    let g:jump = "break"
    exec "source" scriptF
    Xpath 'b'
    let g:jump = "finish"
    exec "source" scriptF
    Xpath 'c'
    try
      let g:jump = "error"
      exec "source" scriptF
      Xpath 'd'
    finally
      Xpath 'e'
      try
        let g:jump = "interrupt"
        exec "source" scriptF
        Xpath 'f'
      finally
        Xpath 'g'
        try
          let g:jump = "throw"
          exec "source" scriptF
          Xpath 'h'
        finally
          Xpath 'i'
        endtry
      endtry
    endtry
    unlet g:jump
    call delete(scriptF)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 38:  :finally reason discarded by an error			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by an error in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_error()
  let test =<< trim [CODE]
    func E(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            asdf	" error; discards jump that caused the :finally
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'a'
      call E("continue")
      call assert_report('should not get here')
    finally
      try
        Xpath 'b'
        call E("break")
        call assert_report('should not get here')
      finally
        try
          Xpath 'c'
          call E("return")
          call assert_report('should not get here')
        finally
          try
            Xpath 'd'
            let g:jump = "finish"
            ExecAsScript E
            call assert_report('should not get here')
          finally
            unlet g:jump
            try
              Xpath 'e'
              call E("error")
              call assert_report('should not get here')
            finally
              try
                Xpath 'f'
                call E("interrupt")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'g'
                  call E("throw")
                  call assert_report('should not get here')
                finally
                  Xpath 'h'
                  delfunction E
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 39:  :finally reason discarded by an interrupt			    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by an interrupt in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discarded_by_interrupt()
  let test =<< trim [CODE]
    func I(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            call interrupt()
            let dummy = 0
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      try
        Xpath 'a'
        call I("continue")
        call assert_report('should not get here')
      finally
        try
          Xpath 'b'
          call I("break")
          call assert_report('should not get here')
        finally
          try
            Xpath 'c'
            call I("return")
            call assert_report('should not get here')
          finally
            try
              Xpath 'd'
              let g:jump = "finish"
              ExecAsScript I
              call assert_report('should not get here')
            finally
              unlet g:jump
              try
                Xpath 'e'
                call I("error")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'f'
                  call I("interrupt")
                  call assert_report('should not get here')
                finally
                  try
                    Xpath 'g'
                    call I("throw")
                    call assert_report('should not get here')
                  finally
                    Xpath 'h'
                    delfunction I
                  endtry
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'A'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghA', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 40:  :finally reason discarded by :throw				    {{{1
"
"	    When a :finally clause is executed due to a :continue, :break,
"	    :return, :finish, error, interrupt or :throw, the jump reason is
"	    discarded by a :throw in the finally clause.
"-------------------------------------------------------------------------------

func Test_finally_discard_by_throw()
  let test =<< trim [CODE]
    func T(jump)
      let loop = 0
      while loop < 2
        let loop = loop + 1
        if loop == 1
          try
            if a:jump == "continue"
              continue
            elseif a:jump == "break"
              break
            elseif a:jump == "return" || a:jump == "finish"
              return
            elseif a:jump == "error"
              asdf
            elseif a:jump == "interrupt"
              call interrupt()
              let dummy = 0
            elseif a:jump == "throw"
              throw "abc"
            endif
          finally
            throw "xyz"	" discards jump that caused the :finally
          endtry
        elseif loop == 2
          call assert_report('should not get here')
        endif
      endwhile
      call assert_report('should not get here')
    endfunc

    try
      Xpath 'a'
      call T("continue")
      call assert_report('should not get here')
    finally
      try
        Xpath 'b'
        call T("break")
        call assert_report('should not get here')
      finally
        try
          Xpath 'c'
          call T("return")
          call assert_report('should not get here')
        finally
          try
            Xpath 'd'
            let g:jump = "finish"
            ExecAsScript T
            call assert_report('should not get here')
          finally
            unlet g:jump
            try
              Xpath 'e'
              call T("error")
              call assert_report('should not get here')
            finally
              try
                Xpath 'f'
                call T("interrupt")
                call assert_report('should not get here')
              finally
                try
                  Xpath 'g'
                  call T("throw")
                  call assert_report('should not get here')
                finally
                  Xpath 'h'
                  delfunction T
                endtry
              endtry
            endtry
          endtry
        endtry
      endtry
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 49:  Throwing exceptions across functions			    {{{1
"
"	    When an exception is thrown but not caught inside a function, the
"	    caller is checked for a matching :catch clause.
"-------------------------------------------------------------------------------

func T49_C()
  try
    Xpath 'a'
    throw "arrgh"
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'b'
  endtry
  Xpath 'c'
endfunc

func T49_T1()
  XloopNEXT
  try
    Xloop 'd'
    throw "arrgh"
    call assert_report('should not get here')
  finally
    Xloop 'e'
  endtry
  Xloop 'f'
endfunc

func T49_T2()
  try
    Xpath 'g'
    call T49_T1()
    call assert_report('should not get here')
  finally
    Xpath 'h'
  endtry
  call assert_report('should not get here')
endfunc

func Test_throw_exception_across_funcs()
  XpathINIT
  XloopINIT
  try
    Xpath 'i'
    call T49_C()            " throw and catch
    Xpath 'j'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'k'
    call T49_T1()  " throw, one level
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'l'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'm'
    call T49_T2()	" throw, two levels
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'n'
  catch /.*/
    call assert_report('should not get here')
  endtry
  Xpath 'o'

  call assert_equal('iabcjkd2e2lmgd3e3hno', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 50:  Throwing exceptions across script files			    {{{1
"
"	    When an exception is thrown but not caught inside a script file,
"	    the sourcing script or function is checked for a matching :catch
"	    clause.
"
"	    This test executes the bodies of the functions C, T1, and T2 from
"	    the previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------

func T50_F()
  try
    Xpath 'A'
    exec "source" g:scriptC
    Xpath 'B'
  catch /.*/
    call assert_report('should not get here')
  endtry

  try
    Xpath 'C'
    exec "source" g:scriptT1
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'D'
  catch /.*/
    call assert_report('should not get here')
  endtry
endfunc

func Test_throw_across_script()
  XpathINIT
  XloopINIT
  let g:scriptC = MakeScript("T49_C")
  let g:scriptT1 = MakeScript("T49_T1")
  let scriptT2 = MakeScript("T49_T2", g:scriptT1)

  try
    Xpath 'E'
    call T50_F()
    Xpath 'F'
    exec "source" scriptT2
    call assert_report('should not get here')
  catch /arrgh/
    Xpath 'G'
  catch /.*/
    call assert_report('should not get here')
  endtry
  Xpath 'H'
  call assert_equal('EAabcBCd2e2DFgd3e3hGH', g:Xpath)

  call delete(g:scriptC)
  call delete(g:scriptT1)
  call delete(scriptT2)
  unlet g:scriptC g:scriptT1 scriptT2
endfunc

"-------------------------------------------------------------------------------
" Test 52:  Uncaught exceptions						    {{{1
"
"	    When an exception is thrown but not caught, an error message is
"	    displayed when the script is terminated.  In case of an interrupt
"	    or error exception, the normal interrupt or error message(s) are
"	    displayed.
"-------------------------------------------------------------------------------

func Test_uncaught_exception_1()
  CheckEnglish

  let test =<< trim [CODE]
    Xpath 'a'
    throw "arrgh"
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: arrgh', v:errmsg)
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_2()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "oops"
      call assert_report('should not get here')`
    catch /arrgh/
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: oops', v:errmsg)
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_3()
  CheckEnglish

  let test =<< trim [CODE]
    func T()
      Xpath 'c'
      throw "brrr"
      call assert_report('should not get here')`
    endfunc

    try
      Xpath 'a'
      throw "arrgh"
      call assert_report('should not get here')`
    catch /.*/
      Xpath 'b'
      call T()
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_4()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      throw "arrgh"
      call assert_report('should not get here')`
    finally
      Xpath 'b'
      throw "brrr"
      call assert_report('should not get here')`
    endtry
    call assert_report('should not get here')`
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('E605: Exception not caught: brrr', v:errmsg)
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_5()
  CheckEnglish

  " Need to catch and handle interrupt, otherwise the test will wait for the
  " user to press <Enter> to continue
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        call interrupt()
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /^Vim:Interrupt$/
      Xpath 'b'
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_6()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      let x = novar	" error E121; exception: E121
    catch /E15:/	" should not catch
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
    call assert_equal('E121: Undefined variable: novar', v:errmsg)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_uncaught_exception_7()
  CheckEnglish

  let test =<< trim [CODE]
    try
      Xpath 'a'
      " error E108/E488; exception: E488
      unlet novar #
    catch /E108:/       " should not catch
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
    call assert_equal('E488: Trailing characters: #', v:errmsg)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 53:  Nesting errors: :endif/:else/:elseif			    {{{1
"
"	    For nesting errors of :if conditionals the correct error messages
"	    should be given.
"-------------------------------------------------------------------------------

func Test_nested_if_else_errors()
  CheckEnglish

  " :endif without :if
  let code =<< trim END
    endif
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    while 1
      endif
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
    finally
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :endif without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      endif
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endif):E580: :endif without :if')

  " :else without :if
  let code =<< trim END
    else
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    while 1
      else
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
    finally
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :else without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      else
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E581: :else without :if')

  " :elseif without :if
  let code =<< trim END
    elseif 1
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    while 1
      elseif 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
    finally
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " :elseif without :if
  let code =<< trim END
    try
      throw "a"
    catch /a/
      elseif 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if')

  " multiple :else
  let code =<< trim END
    if 1
    else
    else
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(else):E583: Multiple :else')

  " :elseif after :else
  let code =<< trim END
    if 1
    else
    elseif 1
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(elseif):E584: :elseif after :else')
endfunc

"-------------------------------------------------------------------------------
" Test 54:  Nesting errors: :while/:endwhile				    {{{1
"
"	    For nesting errors of :while conditionals the correct error messages
"	    should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function checks the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_while_error()
  CheckEnglish

  " :endwhile without :while
  let code =<< trim END
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    if 1
      endwhile
    endif
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " Missing :endif
  let code =<< trim END
    while 1
      if 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')

  " :endwhile without :while
  let code =<< trim END
    try
    finally
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " Missing :endtry
  let code =<< trim END
    while 1
      try
      finally
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')

  " Missing :endtry
  let code =<< trim END
    while 1
      if 1
        try
        finally
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E600: Missing :endtry')

  " Missing :endif
  let code =<< trim END
    while 1
      try
      finally
        if 1
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E171: Missing :endif')

  " :endwhile without :while
  let code =<< trim END
    try
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    while 1
      try
        endwhile
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      endwhile
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')

  " :endwhile without :while
  let code =<< trim END
    while 1
      try
        throw "a"
      catch /a/
        endwhile
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endwhile):E588: :endwhile without :while')
endfunc

"-------------------------------------------------------------------------------
" Test 55:  Nesting errors: :continue/:break				    {{{1
"
"	    For nesting errors of :continue and :break commands the correct
"	    error messages should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function checks the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_cont_break_error()
  CheckEnglish

  " :continue without :while
  let code =<< trim END
    continue
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    if 1
      continue
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
    finally
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :continue without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      continue
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(continue):E586: :continue without :while or :for')

  " :break without :while
  let code =<< trim END
    break
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    if 1
      break
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
    finally
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')

  " :break without :while
  let code =<< trim END
    try
      throw "a"
    catch /a/
      break
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(break):E587: :break without :while or :for')
endfunc

"-------------------------------------------------------------------------------
" Test 56:  Nesting errors: :endtry					    {{{1
"
"	    For nesting errors of :try conditionals the correct error messages
"	    should be given.
"
"	    This test reuses the function MESSAGES() from the previous test.
"	    This function check the messages in g:msgfile.
"-------------------------------------------------------------------------------

func Test_nested_endtry_error()
  CheckEnglish

  " :endtry without :try
  let code =<< trim END
    endtry
  END
  call writefile(code, 'Xtest', 'D')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " :endtry without :try
  let code =<< trim END
    if 1
      endtry
    endif
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " :endtry without :try
  let code =<< trim END
    while 1
      endtry
    endwhile
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E602: :endtry without :try')

  " Missing :endif
  let code =<< trim END
    try
        if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')

  " Missing :endif
  let code =<< trim END
    try
    finally
      if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
    finally
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')

  " Missing :endif
  let code =<< trim END
    try
      throw "a"
    catch /a/
      if 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E171: Missing :endif')

  " Missing :endwhile
  let code =<< trim END
    try
      throw "a"
    catch /a/
      while 1
    endtry
  END
  call writefile(code, 'Xtest')
  call AssertException(['source Xtest'], 'Vim(endtry):E170: Missing :endwhile')
endfunc

"-------------------------------------------------------------------------------
" Test 57:  v:exception and v:throwpoint for user exceptions		    {{{1
"
"	    v:exception evaluates to the value of the exception that was caught
"	    most recently and is not finished.  (A caught exception is finished
"	    when the next ":catch", ":finally", or ":endtry" is reached.)
"	    v:throwpoint evaluates to the script/function name and line number
"	    where that exception has been thrown.
"-------------------------------------------------------------------------------

func Test_user_exception_info()
  CheckEnglish

  XpathINIT
  XloopINIT

  func FuncException()
    let g:exception = v:exception
  endfunc

  func FuncThrowpoint()
    let g:throwpoint = v:throwpoint
  endfunc

  let scriptException  = MakeScript("FuncException")
  let scriptThrowPoint = MakeScript("FuncThrowpoint")

  command! CmdException  let g:exception  = v:exception
  command! CmdThrowpoint let g:throwpoint = v:throwpoint

  func T(arg, line)
    if a:line == 2
      throw a:arg		" in line 2
    elseif a:line == 4
      throw a:arg		" in line 4
    elseif a:line == 6
      throw a:arg		" in line 6
    elseif a:line == 8
      throw a:arg		" in line 8
    endif
  endfunc

  func G(arg, line)
    call T(a:arg, a:line)
  endfunc

  func F(arg, line)
    call G(a:arg, a:line)
  endfunc

  let scriptT = MakeScript("T")
  let scriptG = MakeScript("G", scriptT)
  let scriptF = MakeScript("F", scriptG)

  try
    Xpath 'a'
    call F("oops", 2)
  catch /.*/
    Xpath 'b'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    exec "let exception  = v:exception"
    exec "let throwpoint = v:throwpoint"
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    CmdException
    CmdThrowpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    call FuncException()
    call FuncThrowpoint()
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    exec "source" scriptException
    exec "source" scriptThrowPoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)

    try
      Xpath 'c'
      call G("arrgh", 4)
    catch /.*/
      Xpath 'd'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("arrgh", v:exception)
      call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<4\>', v:throwpoint)

      try
        Xpath 'e'
        let g:arg = "autsch"
        let g:line = 6
        exec "source" scriptF
      catch /.*/
        Xpath 'f'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("autsch", v:exception)
        call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
        call assert_match('\<6\>', v:throwpoint)
      finally
        Xpath 'g'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("arrgh", v:exception)
        call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
        call assert_match('\<4\>', v:throwpoint)
        try
          Xpath 'h'
          let g:arg = "brrrr"
          let g:line = 8
          exec "source" scriptG
        catch /.*/
          Xpath 'i'
          let exception  = v:exception
          let throwpoint = v:throwpoint
          " Resolve scriptT for matching it against v:throwpoint.
          call assert_equal("brrrr", v:exception)
          call assert_match(fnamemodify(scriptT, ':t'), v:throwpoint)
          call assert_match('\<8\>', v:throwpoint)
        finally
          Xpath 'j'
          let exception  = v:exception
          let throwpoint = v:throwpoint
          call assert_equal("arrgh", v:exception)
          call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
          call assert_match('\<4\>', v:throwpoint)
        endtry
        Xpath 'k'
        let exception  = v:exception
        let throwpoint = v:throwpoint
        call assert_equal("arrgh", v:exception)
        call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
        call assert_match('\<4\>', v:throwpoint)
      endtry
      Xpath 'l'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("arrgh", v:exception)
      call assert_match('\<G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<4\>', v:throwpoint)
    finally
      Xpath 'm'
      let exception  = v:exception
      let throwpoint = v:throwpoint
      call assert_equal("oops", v:exception)
      call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
      call assert_match('\<2\>', v:throwpoint)
    endtry
    Xpath 'n'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("oops", v:exception)
    call assert_match('\<F\[1]\.\.G\[1]\.\.T\>', v:throwpoint)
    call assert_match('\<2\>', v:throwpoint)
  finally
    Xpath 'o'
    let exception  = v:exception
    let throwpoint = v:throwpoint
    call assert_equal("", v:exception)
    call assert_match('^$', v:throwpoint)
    call assert_match('^$', v:throwpoint)
  endtry

  call assert_equal('abcdefghijklmno', g:Xpath)

  unlet exception throwpoint
  delfunction FuncException
  delfunction FuncThrowpoint
  call delete(scriptException)
  call delete(scriptThrowPoint)
  unlet scriptException scriptThrowPoint
  delcommand CmdException
  delcommand CmdThrowpoint
  delfunction T
  delfunction G
  delfunction F
  call delete(scriptT)
  call delete(scriptG)
  call delete(scriptF)
  unlet scriptT scriptG scriptF
endfunc

"-------------------------------------------------------------------------------
"
" Test 58:  v:exception and v:throwpoint for error/interrupt exceptions	    {{{1
"
"	    v:exception and v:throwpoint work also for error and interrupt
"	    exceptions.
"-------------------------------------------------------------------------------

func Test_exception_info_for_error()
  CheckEnglish

  let test =<< trim [CODE]
    func T(line)
      if a:line == 2
        delfunction T		" error (function in use) in line 2
      elseif a:line == 4
        call interrupt()
      endif
    endfunc

    while 1
      try
        Xpath 'a'
        call T(2)
        call assert_report('should not get here')
      catch /.*/
        Xpath 'b'
        if v:exception !~ 'Vim(delfunction):'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<T\>'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<2\>'
          call assert_report('should not get here')
        endif
      finally
        Xpath 'c'
        if v:exception != ""
          call assert_report('should not get here')
        endif
        if v:throwpoint != ""
          call assert_report('should not get here')
        endif
        break
      endtry
    endwhile

    Xpath 'd'
    if v:exception != ""
      call assert_report('should not get here')
    endif
    if v:throwpoint != ""
      call assert_report('should not get here')
    endif

    while 1
      try
        Xpath 'e'
        call T(4)
        call assert_report('should not get here')
      catch /.*/
        Xpath 'f'
        if v:exception != 'Vim:Interrupt'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ 'function T'
          call assert_report('should not get here')
        endif
        if v:throwpoint !~ '\<4\>'
          call assert_report('should not get here')
        endif
      finally
        Xpath 'g'
        if v:exception != ""
          call assert_report('should not get here')
        endif
        if v:throwpoint != ""
          call assert_report('should not get here')
        endif
        break
      endtry
    endwhile

    Xpath 'h'
    if v:exception != ""
      call assert_report('should not get here')
    endif
    if v:throwpoint != ""
      call assert_report('should not get here')
    endif
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefgh', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
"
" Test 59:  v:exception and v:throwpoint when discarding exceptions	    {{{1
"
"	    When a :catch clause is left by a ":break" etc or an error or
"	    interrupt exception, v:exception and v:throwpoint are reset.  They
"	    are not affected by an exception that is discarded before being
"	    caught.
"-------------------------------------------------------------------------------
func Test_exception_info_on_discard()
  CheckEnglish

  let test =<< trim [CODE]
    let sfile = expand("<sfile>")

    while 1
      try
        throw "x1"
      catch /.*/
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        throw "x2"
      catch /.*/
        break
      finally
        call assert_equal('', v:exception)
        call assert_equal('', v:throwpoint)
      endtry
      break
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        let errcaught = 0
        try
          try
            throw "x3"
          catch /.*/
            let lnum = expand("<sflnum>")
            asdf
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim:E492: Not an editor command:', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'a'

    while 1
      try
        let intcaught = 0
        try
          try
            throw "x4"
          catch /.*/
            let lnum = expand("<sflnum>")
            call interrupt()
          endtry
        catch /.*/
          let intcaught = 1
          call assert_match('Vim:Interrupt', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, intcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'b'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x5"
            " missing endif
          catch /.*/
            call assert_report('should not get here')
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(catch):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'c'

    try
      while 1
        try
          throw "x6"
        finally
          break
        endtry
        break
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    try
      while 1
        try
          throw "x7"
        finally
          break
        endtry
        break
      endwhile
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal('', v:exception)
      call assert_equal('', v:throwpoint)
    endtry
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    while 1
      try
        let errcaught = 0
        try
          try
            throw "x8"
          finally
            let lnum = expand("<sflnum>")
            asdf
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim:E492: Not an editor command:', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'd'

    while 1
      try
        let intcaught = 0
        try
          try
            throw "x9"
          finally
            let lnum = expand("<sflnum>")
            call interrupt()
          endtry
        catch /.*/
          let intcaught = 1
          call assert_match('Vim:Interrupt', v:exception)
          call assert_match('line ' .. (lnum + 1), v:throwpoint)
        endtry
      finally
        call assert_equal(1, intcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'e'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x10"
            " missing endif
          finally
            call assert_equal('', v:exception)
            call assert_equal('', v:throwpoint)
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(finally):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'f'

    while 1
      try
        let errcaught = 0
        try
          try
            if 1
              let lnum = expand("<sflnum>")
              throw "x11"
            " missing endif
          endtry
        catch /.*/
          let errcaught = 1
          call assert_match('Vim(endtry):E171: Missing :endif:', v:exception)
          call assert_match('line ' .. (lnum + 3), v:throwpoint)
        endtry
      finally
        call assert_equal(1, errcaught)
        break
      endtry
    endwhile
    call assert_equal('', v:exception)
    call assert_equal('', v:throwpoint)

    Xpath 'g'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefg', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
"
" Test 60:  (Re)throwing v:exception; :echoerr.				    {{{1
"
"	    A user exception can be rethrown after catching by throwing
"	    v:exception.  An error or interrupt exception cannot be rethrown
"	    because Vim exceptions cannot be faked.  A Vim exception using the
"	    value of v:exception can, however, be triggered by the :echoerr
"	    command.
"-------------------------------------------------------------------------------

func Test_rethrow_exception_1()
  XpathINIT
  try
    try
      Xpath 'a'
      throw "oops"
    catch /oops/
      Xpath 'b'
      throw v:exception	" rethrow user exception
    catch /.*/
      call assert_report('should not get here')
    endtry
  catch /^oops$/			" catches rethrown user exception
    Xpath 'c'
  catch /.*/
    call assert_report('should not get here')
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_2()
  XpathINIT
  try
    let caught = 0
    try
      Xpath 'a'
      write /n/o/n/w/r/i/t/a/b/l/e/_/f/i/l/e
      call assert_report('should not get here')
    catch /^Vim(write):/
      let caught = 1
      throw v:exception	" throw error: cannot fake Vim exception
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, caught)
    endtry
  catch /^Vim(throw):/	" catches throw error
    let caught = caught + 1
  catch /.*/
    call assert_report('should not get here')
  finally
    Xpath 'c'
    call assert_equal(2, caught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_3()
  XpathINIT
  try
    let caught = 0
    try
      Xpath 'a'
      asdf
    catch /^Vim/		" catch error exception
      let caught = 1
      " Trigger Vim error exception with value specified after :echoerr
      let value = substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
      echoerr value
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, caught)
    endtry
  catch /^Vim(echoerr):/
    let caught = caught + 1
    call assert_match(value, v:exception)
  catch /.*/
    call assert_report('should not get here')
  finally
    Xpath 'c'
    call assert_equal(2, caught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

func Test_rethrow_exception_3()
  XpathINIT
  try
    let errcaught = 0
    try
      Xpath 'a'
      let intcaught = 0
      call interrupt()
    catch /^Vim:/		" catch interrupt exception
      let intcaught = 1
      " Trigger Vim error exception with value specified after :echoerr
      echoerr substitute(v:exception, '^Vim\((.*)\)\=:', '', "")
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'b'
      call assert_equal(1, intcaught)
    endtry
  catch /^Vim(echoerr):/
    let errcaught = 1
    call assert_match('Interrupt', v:exception)
  finally
    Xpath 'c'
    call assert_equal(1, errcaught)
  endtry
  call assert_equal('abc', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 61:  Catching interrupt exceptions				    {{{1
"
"	    When an interrupt occurs inside a :try/:endtry region, an
"	    interrupt exception is thrown and can be caught.  Its value is
"	    "Vim:Interrupt".  If the interrupt occurs after an error or a :throw
"	    but before a matching :catch is reached, all following :catches of
"	    that try block are ignored, but the interrupt exception can be
"	    caught by the next surrounding try conditional.  An interrupt is
"	    ignored when there is a previous interrupt that has not been caught
"	    or causes a :finally clause to be executed.
"-------------------------------------------------------------------------------

func Test_catch_intr_exception()
  let test =<< trim [CODE]
    while 1
      try
        try
          Xpath 'a'
          call interrupt()
          call assert_report('should not get here')
        catch /^Vim:Interrupt$/
          Xpath 'b'
        finally
          Xpath 'c'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'd'
        break
      endtry
    endwhile

    while 1
      try
        try
          try
            Xpath 'e'
            asdf
            call assert_report('should not get here')
          catch /do_not_catch/
            call assert_report('should not get here')
          catch /.*/
            Xpath 'f'
            call interrupt()
            call assert_report('should not get here')
          catch /.*/
            call assert_report('should not get here')
          finally
            Xpath 'g'
            call interrupt()
            call assert_report('should not get here')
          endtry
        catch /^Vim:Interrupt$/
          Xpath 'h'
        finally
          Xpath 'i'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'j'
        break
      endtry
    endwhile

    while 1
      try
        try
          try
            Xpath 'k'
            throw "x"
            call assert_report('should not get here')
          catch /do_not_catch/
            call assert_report('should not get here')
          catch /x/
            Xpath 'l'
            call interrupt()
            call assert_report('should not get here')
          catch /.*/
            call assert_report('should not get here')
          endtry
        catch /^Vim:Interrupt$/
          Xpath 'm'
        finally
          Xpath 'n'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'o'
        break
      endtry
    endwhile

    while 1
      try
        try
          Xpath 'p'
          call interrupt()
          call assert_report('should not get here')
        catch /do_not_catch/
          call interrupt()
          call assert_report('should not get here')
        catch /^Vim:Interrupt$/
          Xpath 'q'
        finally
          Xpath 'r'
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 's'
        break
      endtry
    endwhile

    Xpath 't'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmnopqrst', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 62:  Catching error exceptions					    {{{1
"
"	    An error inside a :try/:endtry region is converted to an exception
"	    and can be caught.  The error exception has a "Vim(cmdname):" prefix
"	    where cmdname is the name of the failing command, or a "Vim:" prefix
"	    if no command name is known.  The "Vim" prefixes cannot be faked.
"-------------------------------------------------------------------------------

func Test_catch_err_exception_1()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        unlet novar
      catch /^Vim(unlet):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(unlet):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match('E108: No such variable: "novar"', v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_2()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        throw novar			" error in :throw
      catch /^Vim(throw):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match('E121: Undefined variable: novar', v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_3()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        throw "Vim:faked"		" error: cannot fake Vim exception
      catch /^Vim(throw):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(throw):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E608: Cannot :throw exceptions with 'Vim' prefix",
              \ v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
endfunc

func Test_catch_err_exception_4()
  XpathINIT
  func F()
    while 1
    " Missing :endwhile
  endfunc

  while 1
    try
      try
        let caught = 0
        call F()
      catch /^Vim(endfunction):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(endfunction):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E170: Missing :endwhile", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc F
endfunc

func Test_catch_err_exception_5()
  XpathINIT
  func F()
    while 1
    " Missing :endwhile
  endfunc

  while 1
    try
      try
        let caught = 0
        ExecAsScript F
      catch /^Vim:/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim:', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E170: Missing :endwhile", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc F
endfunc

func Test_catch_err_exception_6()
  XpathINIT
  func G()
    call G()
  endfunc

  while 1
    try
      let mfd_save = &mfd
      set mfd=3
      try
        let caught = 0
        call G()
      catch /^Vim(call):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(call):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      let &mfd = mfd_save
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abc', g:Xpath)
  delfunc G
endfunc

func Test_catch_err_exception_7()
  XpathINIT
  func H()
    return H()
  endfunc

  while 1
    try
      let mfd_save = &mfd
      set mfd=3
      try
        let caught = 0
        call H()
      catch /^Vim(return):/
        Xpath 'a'
        let caught = 1
        let v:errmsg = substitute(v:exception, '^Vim(return):', '', "")
      finally
        Xpath 'b'
        call assert_equal(1, caught)
        call assert_match("E132: Function call depth is higher than 'maxfuncdepth'", v:errmsg)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'c'
      let &mfd = mfd_save
      break		" discard error for $VIMNOERRTHROW
    endtry
    call assert_report('should not get here')
  endwhile

  call assert_equal('abc', g:Xpath)
  delfunc H
endfunc

"-------------------------------------------------------------------------------
" Test 63:  Suppressing error exceptions by :silent!.			    {{{1
"
"	    A :silent! command inside a :try/:endtry region suppresses the
"	    conversion of errors to an exception and the immediate abortion on
"	    error.  When the commands executed by the :silent! themselves open
"	    a new :try/:endtry region, conversion of errors to exception and
"	    immediate abortion is switched on again - until the next :silent!
"	    etc.  The :silent! has the effect of setting v:errmsg to the error
"	    message text (without displaying it) and continuing with the next
"	    script line.
"
"	    When a command triggering autocommands is executed by :silent!
"	    inside a :try/:endtry, the autocommand execution is not suppressed
"	    on error.
"
"	    This test reuses the function MSG() from the previous test.
"-------------------------------------------------------------------------------

func Test_silent_exception()
  XpathINIT
  XloopINIT
  let g:taken = ""

  func S(n) abort
    XloopNEXT
    let g:taken = g:taken . "E" . a:n
    let v:errmsg = ""
    exec "asdf" . a:n

    " Check that ":silent!" continues:
    Xloop 'a'

    " Check that ":silent!" sets "v:errmsg":
    call assert_match("E492: Not an editor command", v:errmsg)
  endfunc

  func Foo()
    while 1
      try
        try
          let caught = 0
          " This is not silent:
          call S(3)
        catch /^Vim:/
          Xpath 'b'
          let caught = 1
          let errmsg3 = substitute(v:exception, '^Vim:', '', "")
          silent! call S(4)
        finally
          call assert_equal(1, caught)
          Xpath 'c'
          call assert_match("E492: Not an editor command", errmsg3)
          silent! call S(5)
          " Break out of try conditionals that cover ":silent!".  This also
          " discards the aborting error when $VIMNOERRTHROW is non-zero.
          break
        endtry
      catch /.*/
        call assert_report('should not get here')
      endtry
    endwhile
    " This is a double ":silent!" (see caller).
    silent! call S(6)
  endfunc

  func Bar()
    try
      silent! call S(2)
      silent! execute "call Foo() | call S(7)"
      silent! call S(8)
    endtry	" normal end of try cond that covers ":silent!"
    " This has a ":silent!" from the caller:
    call S(9)
  endfunc

  silent! call S(1)
  silent! call Bar()
  silent! call S(10)

  call assert_equal("E1E2E3E4E5E6E7E8E9E10", g:taken)

  augroup TMP
    au!
    autocmd BufWritePost * Xpath 'd'
  augroup END

  Xpath 'e'
  silent! write /i/m/p/o/s/s/i/b/l/e
  Xpath 'f'

  call assert_equal('a2a3ba5ca6a7a8a9a10a11edf', g:Xpath)

  augroup TMP
    au!
  augroup END
  augroup! TMP
  delfunction S
  delfunction Foo
  delfunction Bar
endfunc

"-------------------------------------------------------------------------------
" Test 64:  Error exceptions after error, interrupt or :throw		    {{{1
"
"	    When an error occurs after an interrupt or a :throw but before
"	    a matching :catch is reached, all following :catches of that try
"	    block are ignored, but the error exception can be caught by the next
"	    surrounding try conditional.  Any previous error exception is
"	    discarded.  An error is ignored when there is a previous error that
"	    has not been caught.
"-------------------------------------------------------------------------------

func Test_exception_after_error_1()
  XpathINIT
  while 1
    try
      try
        Xpath 'a'
        let caught = 0
        while 1
          if 1
          " Missing :endif
        endwhile	" throw error exception
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_2()
  XpathINIT
  while 1
    try
      try
        Xpath 'a'
        let caught = 0
        try
          if 1
          " Missing :endif
        catch /.*/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_3()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        try
          Xpath 'a'
          call interrupt()
        catch /do_not_catch/
          call assert_report('should not get here')
          if 1
          " Missing :endif
        catch /.*/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_4()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        try
          Xpath 'a'
          throw "x"
        catch /do_not_catch/
          call assert_report('should not get here')
          if 1
          " Missing :endif
        catch /x/	" throw error exception
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
      catch /^Vim(/
        Xpath 'b'
        let caught = 1
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

func Test_exception_after_error_5()
  XpathINIT
  while 1
    try
      try
        let caught = 0
        Xpath 'a'
        endif		" :endif without :if; throw error exception
        if 1
        " Missing :endif
      catch /do_not_catch/ " ignore new error
        call assert_report('should not get here')
      catch /^Vim(endif):/
        Xpath 'b'
        let caught = 1
      catch /^Vim(/
        call assert_report('should not get here')
      finally
        Xpath 'c'
        call assert_equal(1, caught)
      endtry
    catch /.*/
      call assert_report('should not get here')
    finally
      Xpath 'd'
      break
    endtry
    call assert_report('should not get here')
  endwhile
  call assert_equal('abcd', g:Xpath)
endfunc

"-------------------------------------------------------------------------------
" Test 65:  Errors in the /pattern/ argument of a :catch		    {{{1
"
"	    On an error in the /pattern/ argument of a :catch, the :catch does
"	    not match.  Any following :catches of the same :try/:endtry don't
"	    match either.  Finally clauses are executed.
"-------------------------------------------------------------------------------

func Test_catch_pattern_error()
  CheckEnglish
  XpathINIT

  try
    try
      Xpath 'a'
      throw "oops"
    catch /^oops$/
      Xpath 'b'
    catch /\)/		" not checked; exception has already been caught
      call assert_report('should not get here')
    endtry
    Xpath 'c'
  catch /.*/
    call assert_report('should not get here')
  endtry
  call assert_equal('abc', g:Xpath)

  XpathINIT
  func F()
    try
      try
        try
          Xpath 'a'
          throw "ab"
        catch /abc/	" does not catch
          call assert_report('should not get here')
        catch /\)/	" error; discards exception
          call assert_report('should not get here')
        catch /.*/	" not checked
          call assert_report('should not get here')
        finally
          Xpath 'b'
        endtry
        call assert_report('should not get here')
      catch /^ab$/	" checked, but original exception is discarded
        call assert_report('should not get here')
      catch /^Vim(catch):/
        Xpath 'c'
        call assert_match('Vim(catch):E475: Invalid argument:', v:exception)
      finally
        Xpath 'd'
      endtry
      Xpath 'e'
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'f'
  endfunc

  call F()
  call assert_equal('abcdef', g:Xpath)

  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 66:  Stop range :call on error, interrupt, or :throw		    {{{1
"
"	    When a function which is multiply called for a range since it
"	    doesn't handle the range itself has an error in a command
"	    dynamically enclosed by :try/:endtry or gets an interrupt or
"	    executes a :throw, no more calls for the remaining lines in the
"	    range are made.  On an error in a command not dynamically enclosed
"	    by :try/:endtry, the function is executed again for the remaining
"	    lines in the range.
"-------------------------------------------------------------------------------

func Test_stop_range_on_error()
  let test =<< trim [CODE]
    let file = tempname()
    exec "edit" file
    call setline(1, ['line 1', 'line 2', 'line 3'])
    let taken = ""
    let expected = "G1EF1E(1)F1E(2)F1E(3)G2EF2E(1)G3IF3I(1)G4TF4T(1)G5AF5A(1)"

    func F(reason, n) abort
      let g:taken = g:taken .. "F" .. a:n ..
                          \ substitute(a:reason, '\(\l\).*', '\u\1', "") ..
                          \ "(" .. line(".") .. ")"

      if a:reason == "error"
        asdf
      elseif a:reason == "interrupt"
        call interrupt()
      elseif a:reason == "throw"
        throw "xyz"
      elseif a:reason == "aborting error"
        XloopNEXT
        call assert_equal(g:taken, g:expected)
        try
          bwipeout!
          call delete(g:file)
          asdf
        endtry
      endif
    endfunc

    func G(reason, n)
      let g:taken = g:taken .. "G" .. a:n ..
                              \ substitute(a:reason, '\(\l\).*', '\u\1', "")
      1,3call F(a:reason, a:n)
    endfunc

    Xpath 'a'
    call G("error", 1)
    try
      Xpath 'b'
      try
        call G("error", 2)
        call assert_report('should not get here')
      finally
        Xpath 'c'
        try
          call G("interrupt", 3)
          call assert_report('should not get here')
        finally
          Xpath 'd'
          try
            call G("throw", 4)
            call assert_report('should not get here')
          endtry
        endtry
      endtry
    catch /xyz/
      Xpath 'e'
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'f'
    call G("aborting error", 5)
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdef', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 67:  :throw across :call command					    {{{1
"
"	    On a call command, an exception might be thrown when evaluating the
"	    function name, during evaluation of the arguments, or when the
"	    function is being executed.  The exception can be caught by the
"	    caller.
"-------------------------------------------------------------------------------

func THROW(x, n)
  if a:n == 1
    Xpath 'A'
  elseif a:n == 2
    Xpath 'B'
  elseif a:n == 3
    Xpath 'C'
  endif
  throw a:x
endfunc

func NAME(x, n)
  if a:n == 1
    call assert_report('should not get here')
  elseif a:n == 2
    Xpath 'D'
  elseif a:n == 3
    Xpath 'E'
  elseif a:n == 4
    Xpath 'F'
  endif
  return a:x
endfunc

func ARG(x, n)
  if a:n == 1
    call assert_report('should not get here')
  elseif a:n == 2
    call assert_report('should not get here')
  elseif a:n == 3
    Xpath 'G'
  elseif a:n == 4
    Xpath 'I'
  endif
  return a:x
endfunc

func Test_throw_across_call_cmd()
  XpathINIT

  func F(x, n)
    if a:n == 2
      call assert_report('should not get here')
    elseif a:n == 4
      Xpath 'a'
    endif
  endfunc

  while 1
    try
      let v:errmsg = ""

      while 1
        try
          Xpath 'b'
          call {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
          call assert_report('should not get here')
        catch /^name$/
          Xpath 'c'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'd'
          call {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
          call assert_report('should not get here')
        catch /^arg$/
          Xpath 'e'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'f'
          call {NAME("THROW", 3)}(ARG("call", 3), 3)
          call assert_report('should not get here')
        catch /^call$/
          Xpath 'g'
        catch /^0$/	    " default return value
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'h'
          call {NAME("F", 4)}(ARG(4711, 4), 4)
          Xpath 'i'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile

    catch /^0$/	    " default return value
      call assert_report('should not get here')
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal("", v:errmsg)
      let v:errmsg = ""
      break
    endtry
  endwhile

  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
  delfunction F
endfunc

"-------------------------------------------------------------------------------
" Test 68:  :throw across function calls in expressions			    {{{1
"
"	    On a function call within an expression, an exception might be
"	    thrown when evaluating the function name, during evaluation of the
"	    arguments, or when the function is being executed.  The exception
"	    can be caught by the caller.
"
"	    This test reuses the functions THROW(), NAME(), and ARG() from the
"	    previous test.
"-------------------------------------------------------------------------------

func Test_throw_across_call_expr()
  XpathINIT

  func F(x, n)
    if a:n == 2
      call assert_report('should not get here')
    elseif a:n == 4
      Xpath 'a'
    endif
    return a:x
  endfunction

  while 1
    try
      let error = 0
      let v:errmsg = ""

      while 1
        try
          Xpath 'b'
          let var1 = {NAME(THROW("name", 1), 1)}(ARG(4711, 1), 1)
          call assert_report('should not get here')
        catch /^name$/
          Xpath 'c'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var1'))

      while 1
        try
          Xpath 'd'
          let var2 = {NAME("F", 2)}(ARG(THROW("arg", 2), 2), 2)
          call assert_report('should not get here')
        catch /^arg$/
          Xpath 'e'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var2'))

      while 1
        try
          Xpath 'f'
          let var3 = {NAME("THROW", 3)}(ARG("call", 3), 3)
          call assert_report('should not get here')
        catch /^call$/
          Xpath 'g'
        catch /^0$/	    " default return value
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(!exists('var3'))

      while 1
        try
          Xpath 'h'
          let var4 = {NAME("F", 4)}(ARG(4711, 4), 4)
          Xpath 'i'
        catch /.*/
          call assert_report('should not get here')
        finally
          call assert_equal("", v:errmsg)
          let v:errmsg = ""
          break
        endtry
      endwhile
      call assert_true(exists('var4') && var4 == 4711)

    catch /^0$/	    " default return value
      call assert_report('should not get here')
    catch /.*/
      call assert_report('should not get here')
    finally
      call assert_equal("", v:errmsg)
      break
    endtry
  endwhile

  call assert_equal('bAcdDBefEGCghFIai', g:Xpath)
  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 76:  Errors, interrupts, :throw during expression evaluation	    {{{1
"
"	    When a function call made during expression evaluation is aborted
"	    due to an error inside a :try/:endtry region or due to an interrupt
"	    or a :throw, the expression evaluation is aborted as well.	No
"	    message is displayed for the cancelled expression evaluation.  On an
"	    error not inside :try/:endtry, the expression evaluation continues.
"-------------------------------------------------------------------------------

func Test_expr_eval_error()
  let test =<< trim [CODE]
    let taken = ""

    func ERR(n)
      let g:taken = g:taken .. "E" .. a:n
      asdf
    endfunc

    func ERRabort(n) abort
      let g:taken = g:taken .. "A" .. a:n
      asdf
    endfunc	" returns -1; may cause follow-up msg for illegal var/func name

    func WRAP(n, arg)
      let g:taken = g:taken .. "W" .. a:n
      let g:saved_errmsg = v:errmsg
      return arg
    endfunc

    func INT(n)
      let g:taken = g:taken .. "I" .. a:n
      call interrupt()
    endfunc

    func THR(n)
      let g:taken = g:taken .. "T" .. a:n
      throw "should not be caught"
    endfunc

    func CONT(n)
      let g:taken = g:taken .. "C" .. a:n
    endfunc

    func MSG(n)
      let g:taken = g:taken .. "M" .. a:n
      let errmsg = (a:n >= 37 && a:n <= 44) ? g:saved_errmsg : v:errmsg
      let msgptn = (a:n >= 10 && a:n <= 27) ? "^$" : "asdf"
      call assert_match(msgptn, errmsg)
      let v:errmsg = ""
      let g:saved_errmsg = ""
    endfunc

    let v:errmsg = ""

    try
      let t = 1
      while t <= 9
        Xloop 'a'
        try
          if t == 1
            let v{ERR(t) + CONT(t)} = 0
          elseif t == 2
            let v{ERR(t) + CONT(t)}
          elseif t == 3
            let var = exists('v{ERR(t) + CONT(t)}')
          elseif t == 4
            unlet v{ERR(t) + CONT(t)}
          elseif t == 5
            function F{ERR(t) + CONT(t)}()
            endfunction
          elseif t == 6
            function F{ERR(t) + CONT(t)}
          elseif t == 7
            let var = exists('*F{ERR(t) + CONT(t)}')
          elseif t == 8
            delfunction F{ERR(t) + CONT(t)}
          elseif t == 9
            let var = ERR(t) + CONT(t)
          endif
        catch /asdf/
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim:', '', "")
        catch /^Vim\((\a\+)\)\=:/
          " An error exception has been thrown after the original error.
          let v:errmsg = ""
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard an aborting error
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    try
      let t = 10
      while t <= 18
        Xloop 'b'
        try
          if t == 10
            let v{INT(t) + CONT(t)} = 0
          elseif t == 11
            let v{INT(t) + CONT(t)}
          elseif t == 12
            let var = exists('v{INT(t) + CONT(t)}')
          elseif t == 13
            unlet v{INT(t) + CONT(t)}
          elseif t == 14
            function F{INT(t) + CONT(t)}()
            endfunction
          elseif t == 15
            function F{INT(t) + CONT(t)}
          elseif t == 16
            let var = exists('*F{INT(t) + CONT(t)}')
          elseif t == 17
            delfunction F{INT(t) + CONT(t)}
          elseif t == 18
            let var = INT(t) + CONT(t)
          endif
        catch /^Vim\((\a\+)\)\=:\(Interrupt\)\@!/
          " An error exception has been triggered after the interrupt.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard interrupt
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    try
      let t = 19
      while t <= 27
        Xloop 'c'
        try
          if t == 19
            let v{THR(t) + CONT(t)} = 0
          elseif t == 20
            let v{THR(t) + CONT(t)}
          elseif t == 21
            let var = exists('v{THR(t) + CONT(t)}')
          elseif t == 22
            unlet v{THR(t) + CONT(t)}
          elseif t == 23
            function F{THR(t) + CONT(t)}()
            endfunction
          elseif t == 24
            function F{THR(t) + CONT(t)}
          elseif t == 25
            let var = exists('*F{THR(t) + CONT(t)}')
          elseif t == 26
            delfunction F{THR(t) + CONT(t)}
          elseif t == 27
            let var = THR(t) + CONT(t)
          endif
        catch /^Vim\((\a\+)\)\=:/
          " An error exception has been triggered after the :throw.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          call MSG(t)
          let t = t + 1
          XloopNEXT
          continue	" discard exception
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    let v{ERR(28) + CONT(28)} = 0
    call MSG(28)
    let v{ERR(29) + CONT(29)}
    call MSG(29)
    let var = exists('v{ERR(30) + CONT(30)}')
    call MSG(30)
    unlet v{ERR(31) + CONT(31)}
    call MSG(31)
    function F{ERR(32) + CONT(32)}()
    endfunction
    call MSG(32)
    function F{ERR(33) + CONT(33)}
    call MSG(33)
    let var = exists('*F{ERR(34) + CONT(34)}')
    call MSG(34)
    delfunction F{ERR(35) + CONT(35)}
    call MSG(35)
    let var = ERR(36) + CONT(36)
    call MSG(36)

    let saved_errmsg = ""

    let v{WRAP(37, ERRabort(37)) + CONT(37)} = 0
    call MSG(37)
    let v{WRAP(38, ERRabort(38)) + CONT(38)}
    call MSG(38)
    let var = exists('v{WRAP(39, ERRabort(39)) + CONT(39)}')
    call MSG(39)
    unlet v{WRAP(40, ERRabort(40)) + CONT(40)}
    call MSG(40)
    function F{WRAP(41, ERRabort(41)) + CONT(41)}()
    endfunction
    call MSG(41)
    function F{WRAP(42, ERRabort(42)) + CONT(42)}
    call MSG(42)
    let var = exists('*F{WRAP(43, ERRabort(43)) + CONT(43)}')
    call MSG(43)
    delfunction F{WRAP(44, ERRabort(44)) + CONT(44)}
    call MSG(44)
    let var = ERRabort(45) + CONT(45)
    call MSG(45)
    Xpath 'd'

    let expected = ""
          \ .. "E1M1E2M2E3M3E4M4E5M5E6M6E7M7E8M8E9M9"
          \ .. "I10M10I11M11I12M12I13M13I14M14I15M15I16M16I17M17I18M18"
          \ .. "T19M19T20M20T21M21T22M22T23M23T24M24T25M25T26M26T27M27"
          \ .. "E28C28M28E29C29M29E30C30M30E31C31M31E32C32M32E33C33M33"
          \ .. "E34C34M34E35C35M35E36C36M36"
          \ .. "A37W37C37M37A38W38C38M38A39W39C39M39A40W40C40M40A41W41C41M41"
          \ .. "A42W42C42M42A43W43C43M43A44W44C44M44A45C45M45"
    call assert_equal(expected, taken)
  [CODE]
  let verify =<< trim [CODE]
    let expected = "a1a2a3a4a5a6a7a8a9"
                      \ .. "b10b11b12b13b14b15b16b17b18"
                      \ .. "c19c20c21c22c23c24c25c26c27d"
    call assert_equal(expected, g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 77:  Errors, interrupts, :throw in name{brace-expression}	    {{{1
"
"	    When a function call made during evaluation of an expression in
"	    braces as part of a function name after ":function" is aborted due
"	    to an error inside a :try/:endtry region or due to an interrupt or
"	    a :throw, the expression evaluation is aborted as well, and the
"	    function definition is ignored, skipping all commands to the
"	    ":endfunction".  On an error not inside :try/:endtry, the expression
"	    evaluation continues and the function gets defined, and can be
"	    called and deleted.
"-------------------------------------------------------------------------------
func Test_brace_expr_error()
  let test =<< trim [CODE]
    func ERR() abort
      Xloop 'a'
      asdf
    endfunc					" returns -1

    func OK()
      Xloop 'b'
      let v:errmsg = ""
      return 0
    endfunc

    let v:errmsg = ""

    Xpath 'c'
    func F{1 + ERR() + OK()}(arg)
      " F0 should be defined.
      if exists("a:arg") && a:arg == "calling"
        Xpath 'd'
      else
        call assert_report('should not get here')
      endif
    endfunction
    call assert_equal("", v:errmsg)
    XloopNEXT

    Xpath 'e'
    call F{1 + ERR() + OK()}("calling")
    call assert_equal("", v:errmsg)
    XloopNEXT

    Xpath 'f'
    delfunction F{1 + ERR() + OK()}
    call assert_equal("", v:errmsg)
    XloopNEXT

    try
      while 1
        try
          Xpath 'g'
          func G{1 + ERR() + OK()}(arg)
            " G0 should not be defined, and the function body should be
            " skipped.
            call assert_report('should not get here')
            " Use an unmatched ":finally" to check whether the body is
            " skipped when an error occurs in ERR().  This works whether or
            " not the exception is converted to an exception.
            finally
              call assert_report('should not get here')
            endtry
          try
            call assert_report('should not get here')
          endfunction

          call assert_report('should not get here')
        catch /asdf/
          " Jumped to when the function is not defined and the body is
          " skipped.
          Xpath 'h'
        catch /.*/
          call assert_report('should not get here')
        finally
          Xpath 'i'
          break
        endtry			" jumped to when the body is not skipped
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ca1b1ea2b2dfa3b3ga4hi', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 78:  Messages on parsing errors in expression evaluation		    {{{1
"
"	    When an expression evaluation detects a parsing error, an error
"	    message is given and converted to an exception, and the expression
"	    evaluation is aborted.
"-------------------------------------------------------------------------------
func Test_expr_eval_error_msg()
  CheckEnglish

  let test =<< trim [CODE]
    let taken = ""

    func F(n)
      let g:taken = g:taken . "F" . a:n
    endfunc

    func MSG(n, enr, emsg)
      let g:taken = g:taken . "M" . a:n
      call assert_match('^' .. a:enr .. ':', v:errmsg)
      call assert_match(a:emsg, v:errmsg)
    endfunc

    func CONT(n)
      let g:taken = g:taken . "C" . a:n
    endfunc

    let v:errmsg = ""
    try
      let t = 1
      while t <= 14
        let g:taken = g:taken . "T" . t
        let v:errmsg = ""
        try
          if t == 1
            let v{novar + CONT(t)} = 0
          elseif t == 2
            let v{novar + CONT(t)}
          elseif t == 3
            let var = exists('v{novar + CONT(t)}')
          elseif t == 4
            unlet v{novar + CONT(t)}
          elseif t == 5
            function F{novar + CONT(t)}()
            endfunction
          elseif t == 6
            function F{novar + CONT(t)}
          elseif t == 7
            let var = exists('*F{novar + CONT(t)}')
          elseif t == 8
            delfunction F{novar + CONT(t)}
          elseif t == 9
            echo novar + CONT(t)
          elseif t == 10
            echo v{novar + CONT(t)}
          elseif t == 11
            echo F{novar + CONT(t)}
          elseif t == 12
            let var = novar + CONT(t)
          elseif t == 13
            let var = v{novar + CONT(t)}
          elseif t == 14
            let var = F{novar + CONT(t)}()
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'a'
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          Xloop 'b'
          if t <= 8 && t != 3 && t != 7
            call MSG(t, 'E475', 'Invalid argument\>')
          else
            call MSG(t, 'E121', "Undefined variable")
          endif
          let t = t + 1
          XloopNEXT
          continue	" discard an aborting error
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry

    func T(n, expr, enr, emsg)
      try
        let g:taken = g:taken . "T" . a:n
        let v:errmsg = ""
        try
          execute "let var = " . a:expr
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'c'
          " v:errmsg is not set when the error message is converted to an
          " exception.  Set it to the original error message.
          let v:errmsg = substitute(v:exception, '^Vim\((\a\+)\)\=:', '', "")
        finally
          Xloop 'd'
          call MSG(a:n, a:enr, a:emsg)
          XloopNEXT
          " Discard an aborting error:
          return
        endtry
      catch /.*/
        call assert_report('should not get here')
      endtry
    endfunc

    call T(15, 'Nofunc() + CONT(15)',	'E117',	"Unknown function")
    call T(16, 'F(1 2 + CONT(16))',	'E116',	"Invalid arguments")
    call T(17, 'F(1, 2) + CONT(17)',	'E118',	"Too many arguments")
    call T(18, 'F() + CONT(18)',	'E119',	"Not enough arguments")
    call T(19, '{(1} + CONT(19)',	'E110',	"Missing ')'")
    call T(20, '("abc"[1) + CONT(20)',	'E111',	"Missing ']'")
    call T(21, '(1 +) + CONT(21)',	'E15',	"Invalid expression")
    call T(22, '1 2 + CONT(22)',	'E488',	"Trailing characters: 2 +")
    call T(23, '(1 ? 2) + CONT(23)',	'E109',	"Missing ':' after '?'")
    call T(24, '("abc) + CONT(24)',	'E114',	"Missing double quote")
    call T(25, "('abc) + CONT(25)",	'E115',	"Missing single quote")
    call T(26, '& + CONT(26)',		'E112', "Option name missing")
    call T(27, '&asdf + CONT(27)',	'E113', "Unknown option")

    let expected = ""
      \ .. "T1M1T2M2T3M3T4M4T5M5T6M6T7M7T8M8T9M9T10M10T11M11T12M12T13M13T14M14"
      \ .. "T15M15T16M16T17M17T18M18T19M19T20M20T21M21T22M22T23M23T24M24T25M25"
      \ .. "T26M26T27M27"

    call assert_equal(expected, taken)
  [CODE]
  let verify =<< trim [CODE]
    let expected = "a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9a10b10a11b11a12b12"
                  \ .. "a13b13a14b14c15d15c16d16c17d17c18d18c19d19c20d20"
                  \ .. "c21d21c22d22c23d23c24d24c25d25c26d26c27d27"
    call assert_equal(expected, g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 79:  Throwing one of several errors for the same command		    {{{1
"
"	    When several errors appear in a row (for instance during expression
"	    evaluation), the first as the most specific one is used when
"	    throwing an error exception.  If, however, a syntax error is
"	    detected afterwards, this one is used for the error exception.
"	    On a syntax error, the next command is not executed, on a normal
"	    error, however, it is (relevant only in a function without the
"	    "abort" flag).  v:errmsg is not set.
"
"	    If throwing error exceptions is configured off, v:errmsg is always
"	    set to the latest error message, that is, to the more general
"	    message or the syntax error, respectively.
"-------------------------------------------------------------------------------
func Test_throw_multi_error()
  CheckEnglish

  let test =<< trim [CODE]
    func NEXT(cmd)
      exec a:cmd . " | Xloop 'a'"
    endfun

    call NEXT('echo novar')			" (checks nextcmd)
    XloopNEXT
    call NEXT('let novar #')			" (skips nextcmd)
    XloopNEXT
    call NEXT('unlet novar #')			" (skips nextcmd)
    XloopNEXT
    call NEXT('let {novar}')			" (skips nextcmd)
    XloopNEXT
    call NEXT('unlet{ novar}')			" (skips nextcmd)

    call assert_equal('a1', g:Xpath)
    XpathINIT
    XloopINIT

    func EXEC(cmd)
      exec a:cmd
    endfunc

    try
      while 1				" dummy loop
        try
          let v:errmsg = ""
          call EXEC('echo novar')	" normal error
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'b'
          call assert_match('E121: Undefined variable: novar', v:exception)
        finally
          Xpath 'c'
          call assert_equal("", v:errmsg)
          break
        endtry
      endwhile

      Xpath 'd'
      let cmd = "let"
      while cmd != ""
        try
          let v:errmsg = ""
          call EXEC(cmd . ' novar #')		" normal plus syntax error
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'e'
          if cmd =~ 'unlet'
            " TODO: should get error for 'novar'
            call assert_match('E488: Trailing characters', v:exception)
          else
            call assert_match('E121: Undefined variable: novar', v:exception)
          endif
        finally
          Xloop 'f'
          call assert_equal("", v:errmsg)
          if cmd == "let"
            let cmd = "unlet"
          else
            let cmd = ""
          endif
          XloopNEXT
          continue
        endtry
      endwhile

      Xpath 'g'
      let cmd = "let"
      while cmd != ""
        try
          let v:errmsg = ""
          call EXEC(cmd . ' {novar}')		" normal plus syntax error
        catch /^Vim\((\a\+)\)\=:/
          Xloop 'h'
          call assert_match('E475: Invalid argument: {novar}', v:exception)
        finally
          Xloop 'i'
          call assert_equal("", v:errmsg)
          if cmd == "let"
            let cmd = "unlet"
          else
            let cmd = ""
          endif
          XloopNEXT
          continue
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'j'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('bcde1f1e2f2gh3i3h4i4j', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 80:  Syntax error in expression for illegal :elseif		    {{{1
"
"	    If there is a syntax error in the expression after an illegal
"	    :elseif, an error message is given (or an error exception thrown)
"	    for the illegal :elseif rather than the expression error.
"-------------------------------------------------------------------------------
func Test_if_syntax_error()
  CheckEnglish

  let test =<< trim [CODE]
    let v:errmsg = ""
    if 0
    else
    elseif 1 ||| 2
    endif
    Xpath 'a'
    call assert_match('E584: :elseif after :else', v:errmsg)

    let v:errmsg = ""
    if 1
    else
    elseif 1 ||| 2
    endif
    Xpath 'b'
    call assert_match('E584: :elseif after :else', v:errmsg)

    let v:errmsg = ""
    elseif 1 ||| 2
    Xpath 'c'
    call assert_match('E582: :elseif without :if', v:errmsg)

    let v:errmsg = ""
    while 1
      elseif 1 ||| 2
    endwhile
    Xpath 'd'
    call assert_match('E582: :elseif without :if', v:errmsg)

    while 1
      try
        try
          let v:errmsg = ""
          if 0
          else
          elseif 1 ||| 2
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'e'
          call assert_match('E584: :elseif after :else', v:exception)
        finally
          Xpath 'f'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
      call assert_report('should not get here')
      finally
        Xpath 'g'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          if 1
          else
          elseif 1 ||| 2
          endif
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'h'
          call assert_match('E584: :elseif after :else', v:exception)
        finally
          Xpath 'i'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'j'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          elseif 1 ||| 2
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'k'
          call assert_match('E582: :elseif without :if', v:exception)
        finally
          Xpath 'l'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'm'
        break
      endtry
    endwhile

    while 1
      try
        try
          let v:errmsg = ""
          while 1
              elseif 1 ||| 2
          endwhile
        catch /^Vim\((\a\+)\)\=:/
          Xpath 'n'
          call assert_match('E582: :elseif without :if', v:exception)
        finally
          Xpath 'o'
          call assert_equal("", v:errmsg)
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'p'
        break
      endtry
    endwhile
    Xpath 'q'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmnopq', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 81:  Discarding exceptions after an error or interrupt		    {{{1
"
"	    When an exception is thrown from inside a :try conditional without
"	    :catch and :finally clauses and an error or interrupt occurs before
"	    the :endtry is reached, the exception is discarded.
"-------------------------------------------------------------------------------

func Test_discard_exception_after_error_1()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('ab', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt the code before the endtry is invoked
func Test_discard_exception_after_error_2()
  XpathINIT
  let lines =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
      endtry                      " interrupt here
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 7 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 7', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('ab', g:Xpath)
  breakdel *
endfunc

"-------------------------------------------------------------------------------
" Test 82:  Ignoring :catch clauses after an error or interrupt		    {{{1
"
"	    When an exception is thrown and an error or interrupt occurs before
"	    the matching :catch clause is reached, the exception is discarded
"	    and the :catch clause is ignored (also for the error or interrupt
"	    exception being thrown then).
"-------------------------------------------------------------------------------

func Test_ignore_catch_after_error_1()
  let test =<< trim [CODE]
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      catch /.*/
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

func Test_ignore_catch_after_error_2()
  let test =<< trim [CODE]
    func E()
      try
        try
          Xpath 'a'
          throw "arrgh"
          call assert_report('should not get here')
          if 1
          call assert_report('should not get here')
          " error after :throw: missing :endif
        catch /.*/
          call assert_report('should not get here')
        catch /.*/
          call assert_report('should not get here')
        endtry
        call assert_report('should not get here')
      catch /arrgh/
        call assert_report('should not get here')
      endtry
    endfunc

    call E()
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('a', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt right before a catch is invoked in a script
func Test_ignore_catch_after_intr_1()
  " for unknown reasons this test sometimes fails on MS-Windows.
  let g:test_is_flaky = 1

  XpathINIT
  let lines =<< trim [CODE]
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
      catch /.*/              " interrupt here
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 6 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 6', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('a', g:Xpath)
  breakdel *
endfunc

" interrupt right before a catch is invoked inside a function.
func Test_ignore_catch_after_intr_2()
  " for unknown reasons this test sometimes fails on MS-Windows.
  let g:test_is_flaky = 1

  XpathINIT
  func F()
    try
      try
        Xpath 'a'
        throw "arrgh"
        call assert_report('should not get here')
      catch /.*/              " interrupt here
        call assert_report('should not get here')
      catch /.*/
        call assert_report('should not get here')
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  endfunc

  breakadd func 6 F
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('\.F, line 6', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('a', g:Xpath)
  breakdel *
  delfunc F
endfunc

"-------------------------------------------------------------------------------
" Test 83:  Executing :finally clauses after an error or interrupt	    {{{1
"
"	    When an exception is thrown and an error or interrupt occurs before
"	    the :finally of the innermost :try is reached, the exception is
"	    discarded and the :finally clause is executed.
"-------------------------------------------------------------------------------

func Test_finally_after_error()
  let test =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
        if 1
        call assert_report('should not get here')
        " error after :throw: missing :endif
      finally
        Xpath 'c'
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abc', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

" interrupt the code right before the finally is invoked
func Test_finally_after_intr()
  XpathINIT
  let lines =<< trim [CODE]
    try
      Xpath 'a'
      try
        Xpath 'b'
        throw "arrgh"
        call assert_report('should not get here')
      finally		" interrupt here
        Xpath 'c'
      endtry
      call assert_report('should not get here')
    catch /arrgh/
      call assert_report('should not get here')
    endtry
    call assert_report('should not get here')
  [CODE]
  call writefile(lines, 'Xscript', 'D')

  breakadd file 7 Xscript
  try
    let caught_intr = 0
    debuggreedy
    call feedkeys(":source Xscript\<CR>quit\<CR>", "xt")
  catch /^Vim:Interrupt$/
    call assert_match('Xscript, line 7', v:throwpoint)
    let caught_intr = 1
  endtry
  0debuggreedy
  call assert_equal(1, caught_intr)
  call assert_equal('abc', g:Xpath)
  breakdel *
endfunc

"-------------------------------------------------------------------------------
" Test 84:  Exceptions in autocommand sequences.			    {{{1
"
"	    When an exception occurs in a sequence of autocommands for
"	    a specific event, the rest of the sequence is not executed.  The
"	    command that triggered the autocommand execution aborts, and the
"	    exception is propagated to the caller.
"
"	    For the FuncUndefined event under a function call expression or
"	    :call command, the function is not executed, even when it has
"	    been defined by the autocommands before the exception occurred.
"-------------------------------------------------------------------------------

func Test_autocmd_exception()
  let test =<< trim [CODE]
    func INT()
      call interrupt()
    endfunc

    aug TMP
      autocmd!

      autocmd User x1 Xpath 'a'
      autocmd User x1 throw "x1"
      autocmd User x1 call assert_report('should not get here')

      autocmd User x2 Xpath 'b'
      autocmd User x2 asdf
      autocmd User x2 call assert_report('should not get here')

      autocmd User x3 Xpath 'c'
      autocmd User x3 call INT()
      autocmd User x3 call assert_report('should not get here')

      autocmd FuncUndefined U1 func U1()
      autocmd FuncUndefined U1   call assert_report('should not get here')
      autocmd FuncUndefined U1 endfunc
      autocmd FuncUndefined U1 Xpath 'd'
      autocmd FuncUndefined U1 throw "U1"
      autocmd FuncUndefined U1 call assert_report('should not get here')

      autocmd FuncUndefined U2 func U2()
      autocmd FuncUndefined U2   call assert_report('should not get here')
      autocmd FuncUndefined U2 endfunc
      autocmd FuncUndefined U2 Xpath 'e'
      autocmd FuncUndefined U2 ASDF
      autocmd FuncUndefined U2 call assert_report('should not get here')

      autocmd FuncUndefined U3 func U3()
      autocmd FuncUndefined U3   call assert_report('should not get here')
      autocmd FuncUndefined U3 endfunc
      autocmd FuncUndefined U3 Xpath 'f'
      autocmd FuncUndefined U3 call INT()
      autocmd FuncUndefined U3 call assert_report('should not get here')
    aug END

    try
      try
        Xpath 'g'
        doautocmd User x1
      catch /x1/
        Xpath 'h'
      endtry

      while 1
        try
          Xpath 'i'
          doautocmd User x2
        catch /asdf/
          Xpath 'j'
        finally
          Xpath 'k'
          break
        endtry
      endwhile

      while 1
        try
          Xpath 'l'
          doautocmd User x3
        catch /Vim:Interrupt/
          Xpath 'm'
        finally
          Xpath 'n'
          " ... but break loop for caught interrupt exception,
          " or discard interrupt and break loop if $VIMNOINTTHROW
          break
        endtry
      endwhile

      if exists("*U1") | delfunction U1 | endif
      if exists("*U2") | delfunction U2 | endif
      if exists("*U3") | delfunction U3 | endif

      try
        Xpath 'o'
        call U1()
      catch /U1/
        Xpath 'p'
      endtry

      while 1
        try
          Xpath 'q'
          call U2()
        catch /ASDF/
          Xpath 'r'
        finally
          Xpath 's'
          " ... but break loop for caught error exception,
          " or discard error and break loop if $VIMNOERRTHROW
          break
        endtry
      endwhile

      while 1
        try
          Xpath 't'
          call U3()
        catch /Vim:Interrupt/
          Xpath 'u'
        finally
          Xpath 'v'
          " ... but break loop for caught interrupt exception,
          " or discard interrupt and break loop if $VIMNOINTTHROW
          break
        endtry
      endwhile
    catch /.*/
      call assert_report('should not get here')
    endtry
    Xpath 'w'
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('gahibjklcmnodpqerstfuvw', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 85:  Error exceptions in autocommands for I/O command events	    {{{1
"
"	    When an I/O command is inside :try/:endtry, autocommands to be
"	    executed after it should be skipped on an error (exception) in the
"	    command itself or in autocommands to be executed before the command.
"	    In the latter case, the I/O command should not be executed either.
"	    Example 1: BufWritePre, :write, BufWritePost
"	    Example 2: FileReadPre, :read, FileReadPost.
"-------------------------------------------------------------------------------

func Test_autocmd_error_io_exception()
  let test =<< trim [CODE]
    " Remove the autocommands for the events specified as arguments in all used
    " autogroups.
    func Delete_autocommands(...)
      let augfile = tempname()
      while 1
        try
          exec "redir >" . augfile
          aug
          redir END
          exec "edit" augfile
          g/^$/d
          norm G$
          let wrap = "w"
          while search('\%(  \|^\)\@<=.\{-}\%(  \)\@=', wrap) > 0
            let wrap = "W"
            exec "norm y/  \n"
            let argno = 1
            while argno <= a:0
              exec "au!" escape(@", " ") a:{argno}
              let argno = argno + 1
            endwhile
          endwhile
        catch /.*/
        finally
          bwipeout!
          call delete(augfile)
          break
        endtry
      endwhile
    endfunc

    call Delete_autocommands("BufWritePre", "BufWritePost")

    while 1
      try
        try
          let post = 0
          aug TMP
            au! BufWritePost * let post = 1
          aug END
          write /n/o/n/e/x/i/s/t/e/n/t
        catch /^Vim(write):/
          Xpath 'a'
          call assert_match("E212: Can't open file for writing", v:exception)
        finally
          Xpath 'b'
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'c'
        break
      endtry
    endwhile

    while 1
      try
        try
          let post = 0
          aug TMP
            au! BufWritePre  * asdf
            au! BufWritePost * let post = 1
          aug END
          let tmpfile = tempname()
          exec "write" tmpfile
        catch /^Vim\((write)\)\=:/
          Xpath 'd'
          call assert_match('E492: Not an editor command', v:exception)
        finally
          Xpath 'e'
          if filereadable(tmpfile)
            call assert_report('should not get here')
          endif
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'f'
        break
      endtry
    endwhile

    call delete(tmpfile)

    call Delete_autocommands("BufWritePre", "BufWritePost",
          \ "BufReadPre", "BufReadPost", "FileReadPre", "FileReadPost")

    while 1
      try
        try
          let post = 0
          aug TMP
            au! FileReadPost * let post = 1
          aug END
          let caught = 0
          read /n/o/n/e/x/i/s/t/e/n/t
        catch /^Vim(read):/
          Xpath 'g'
          call assert_match("E484: Can't open file", v:exception)
        finally
          Xpath 'h'
          call assert_equal(0, post)
          au! TMP
          aug! TMP
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'i'
        break
      endtry
    endwhile

    while 1
      try
        let infile = tempname()
        let tmpfile = tempname()
        call writefile(["XYZ"], infile)
        exec "edit" tmpfile
        try
          Xpath 'j'
          try
            let post = 0
            aug TMP
              au! FileReadPre  * asdf
              au! FileReadPost * let post = 1
            aug END
            exec "0read" infile
          catch /^Vim\((read)\)\=:/
            Xpath 'k'
            call assert_match('E492: Not an editor command', v:exception)
          finally
            Xpath 'l'
            if getline("1") == "XYZ"
              call assert_report('should not get here')
            endif
            call assert_equal(0, post)
            au! TMP
            aug! TMP
          endtry
        finally
          Xpath 'm'
          bwipeout!
        endtry
      catch /.*/
        call assert_report('should not get here')
      finally
        Xpath 'n'
        break
      endtry
    endwhile

    call delete(infile)
    call delete(tmpfile)
  [CODE]
  let verify =<< trim [CODE]
    call assert_equal('abcdefghijklmn', g:Xpath)
  [CODE]
  call RunInNewVim(test, verify)
endfunc

"-------------------------------------------------------------------------------
" Test 87   using (expr) ? funcref : funcref				    {{{1
"
"	    Vim needs to correctly parse the funcref and even when it does
"	    not execute the funcref, it needs to consume the trailing ()
"-------------------------------------------------------------------------------

func Add2(x1, x2)
  return a:x1 + a:x2
endfu

func GetStr()
  return "abcdefghijklmnopqrstuvwxyp"
endfu

func Test_funcref_with_condexpr()
  call assert_equal(5, function('Add2')(2,3))

  call assert_equal(3, 1 ? function('Add2')(1,2) : function('Add2')(2,3))
  call assert_equal(5, 0 ? function('Add2')(1,2) : function('Add2')(2,3))
  " Make sure, GetStr() still works.
  call assert_equal('abcdefghijk', GetStr()[0:10])
endfunc

" Test 90:  Recognizing {} in variable name.			    {{{1
"-------------------------------------------------------------------------------

func Test_curlies()
    let s:var = 66
    let ns = 's'
    call assert_equal(66, {ns}:var)

    let g:a = {}
    let g:b = 't'
    let g:a[g:b] = 77
    call assert_equal(77, g:a['t'])
endfunc

"-------------------------------------------------------------------------------
" Test 91:  using type().					    {{{1
"-------------------------------------------------------------------------------

func Test_type()
    call assert_equal(0, type(0))
    call assert_equal(1, type(""))
    call assert_equal(2, type(function("tr")))
    call assert_equal(2, type(function("tr", [8])))
    call assert_equal(3, type([]))
    call assert_equal(4, type({}))
    call assert_equal(5, type(0.0))
    call assert_equal(6, type(v:false))
    call assert_equal(6, type(v:true))
    call assert_equal(7, type(v:none))
    call assert_equal(7, type(v:null))
    call assert_equal(8, v:t_job)
    call assert_equal(9, v:t_channel)
    call assert_equal(v:t_number, type(0))
    call assert_equal(v:t_string, type(""))
    call assert_equal(v:t_func, type(function("tr")))
    call assert_equal(v:t_func, type(function("tr", [8])))
    call assert_equal(v:t_list, type([]))
    call assert_equal(v:t_dict, type({}))
    call assert_equal(v:t_float, type(0.0))
    call assert_equal(v:t_bool, type(v:false))
    call assert_equal(v:t_bool, type(v:true))
    call assert_equal(v:t_none, type(v:none))
    call assert_equal(v:t_none, type(v:null))
    call assert_equal(v:t_string, type(test_null_string()))
    call assert_equal(v:t_func, type(test_null_function()))
    call assert_equal(v:t_func, type(test_null_partial()))
    call assert_equal(v:t_list, type(test_null_list()))
    call assert_equal(v:t_dict, type(test_null_dict()))
    if has('job')
      call assert_equal(v:t_job, type(test_null_job()))
    endif
    if has('channel')
      call assert_equal(v:t_channel, type(test_null_channel()))
    endif
    call assert_equal(v:t_blob, type(test_null_blob()))

    call assert_fails("call type(test_void())", ['E340:', 'E685:'])
    call assert_fails("call type(test_unknown())", ['E340:', 'E685:'])

    call assert_equal(0, 0 + v:false)
    call assert_equal(1, 0 + v:true)
    call assert_equal(0, 0 + v:none)
    call assert_equal(0, 0 + v:null)

    call assert_equal('v:false', '' . v:false)
    call assert_equal('v:true', '' . v:true)
    call assert_equal('v:none', '' . v:none)
    call assert_equal('v:null', '' . v:null)

    call assert_true(v:false == 0)
    call assert_false(v:false != 0)
    call assert_true(v:true == 1)
    call assert_false(v:true != 1)
    call assert_false(v:true == v:false)
    call assert_true(v:true != v:false)

    call assert_true(v:null == 0)
    call assert_false(v:null == 1)
    call assert_false(v:null != 0)
    call assert_true(v:none == 0)
    call assert_false(v:none == 1)
    call assert_false(v:none != 0)
    call assert_true(v:null == 0.0)
    call assert_false(v:null == 0.1)
    call assert_false(v:null != 0.0)

    call assert_true(v:false is v:false)
    call assert_true(v:true is v:true)
    call assert_true(v:none is v:none)
    call assert_true(v:null is v:null)

    call assert_false(v:false isnot v:false)
    call assert_false(v:true isnot v:true)
    call assert_false(v:none isnot v:none)
    call assert_false(v:null isnot v:null)

    call assert_false(v:false is 0)
    call assert_false(v:true is 1)
    call assert_false(v:true is v:false)
    call assert_false(v:none is 0)
    call assert_false(v:none is [])
    call assert_false(v:none is {})
    call assert_false(v:none is 'text')
    call assert_false(v:null is 0)
    call assert_false(v:null is v:none)

    call assert_true(v:false isnot 0)
    call assert_true(v:true isnot 1)
    call assert_true(v:true isnot v:false)
    call assert_true(v:none isnot 0)
    call assert_true(v:null isnot 0)
    call assert_true(v:null isnot v:none)

    call assert_equal(v:false, eval(string(v:false)))
    call assert_equal(v:true, eval(string(v:true)))
    call assert_equal(v:none, eval(string(v:none)))
    call assert_equal(v:null, eval(string(v:null)))

    call assert_equal(v:false, copy(v:false))
    call assert_equal(v:true, copy(v:true))
    call assert_equal(v:none, copy(v:none))
    call assert_equal(v:null, copy(v:null))

    call assert_equal([v:false], deepcopy([v:false]))
    call assert_equal([v:true], deepcopy([v:true]))
    call assert_equal([v:none], deepcopy([v:none]))
    call assert_equal([v:null], deepcopy([v:null]))

    call assert_true(empty(v:false))
    call assert_false(empty(v:true))
    call assert_true(empty(v:null))
    call assert_true(empty(v:none))

    func ChangeYourMind()
	try
	    return v:true
	finally
	    return 'something else'
	endtry
    endfunc

    call ChangeYourMind()
endfunc

func Test_typename()
  call assert_equal('number', typename(123))
  call assert_equal('string', typename('x'))
  call assert_equal('list<number>', typename([123]))
  call assert_equal('dict<number>', typename(#{key: 123}))
  call assert_equal('list<dict<number>>', typename([#{key: 123}]))

  let l = []
  let d = #{a: 0}
  let l = [d]
  let l[0].e = #{b: l}
  call assert_equal('list<dict<any>>', typename(l))
  call assert_equal('dict<any>', typename(d))
endfunc

"-------------------------------------------------------------------------------
" Test 92:  skipping code					    {{{1
"-------------------------------------------------------------------------------

func Test_skip()
    let Fn = function('Test_type')
    call assert_false(0 && Fn[1])
    call assert_false(0 && string(Fn))
    call assert_false(0 && len(Fn))
    let l = []
    call assert_false(0 && l[1])
    call assert_false(0 && string(l))
    call assert_false(0 && len(l))
    let f = 1.0
    call assert_false(0 && f[1])
    call assert_false(0 && string(f))
    call assert_false(0 && len(f))
    let sp = v:null
    call assert_false(0 && sp[1])
    call assert_false(0 && string(sp))
    call assert_false(0 && len(sp))

endfunc

"-------------------------------------------------------------------------------
" Test 93:  :echo and string()					    {{{1
"-------------------------------------------------------------------------------

func Test_echo_and_string()
    " String
    let a = 'foo bar'
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["foo bar",
		     \ "'foo bar'"], l)

    " Float
    let a = -1.2e0
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["-1.2",
                     \ "-1.2"], l)

    " Funcref
    redir => result
    echo function('string')
    echo string(function('string'))
    redir END
    let l = split(result, "\n")
    call assert_equal(["string",
		     \ "function('string')"], l)

    " Recursive dictionary
    let a = {}
    let a["a"] = a
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {...}}",
		     \ "{'a': {...}}"], l)

    " Recursive list
    let a = [0]
    let a[0] = a
    redir => result
    echo a
    echo string(a)
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[...]]",
		     \ "[[...]]"], l)

    " Empty dictionaries in a list
    let a = {}
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[{}, {}, {}]",
		     \ "[{}, {}, {}]"], l)

    " Empty dictionaries in a dictionary
    let a = {}
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {}, 'b': {}}",
		     \ "{'a': {}, 'b': {}}"], l)

    " Empty lists in a list
    let a = []
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[], [], []]",
		     \ "[[], [], []]"], l)

    " Empty lists in a dictionary
    let a = []
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': [], 'b': []}",
		     \ "{'a': [], 'b': []}"], l)

    " Dictionaries in a list
    let a = {"one": "yes", "two": "yes", "three": "yes"}
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]",
		     \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l)

    " Dictionaries in a dictionary
    let a = {"one": "yes", "two": "yes", "three": "yes"}
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}",
		     \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l)

    " Lists in a list
    let a = [1, 2, 3]
    redir => result
    echo [a, a, a]
    echo string([a, a, a])
    redir END
    let l = split(result, "\n")
    call assert_equal(["[[1, 2, 3], [...], [...]]",
		     \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l)

    " Lists in a dictionary
    let a = [1, 2, 3]
    let b = {"a": a, "b": a}
    redir => result
    echo b
    echo string(b)
    redir END
    let l = split(result, "\n")
    call assert_equal(["{'a': [1, 2, 3], 'b': [...]}",
		     \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l)

    call assert_fails('echo &:', 'E112:')
    call assert_fails('echo &g:', 'E112:')
    call assert_fails('echo &l:', 'E112:')

endfunc

"-------------------------------------------------------------------------------
" Test 94:  64-bit Numbers					    {{{1
"-------------------------------------------------------------------------------

func Test_num64()
    call assert_notequal( 4294967296, 0)
    call assert_notequal(-4294967296, 0)
    call assert_equal( 4294967296,  0xFFFFffff + 1)
    call assert_equal(-4294967296, -0xFFFFffff - 1)

    call assert_equal( 9223372036854775807,  1 / 0)
    call assert_equal(-9223372036854775807, -1 / 0)
    call assert_equal(-9223372036854775807 - 1,  0 / 0)

    call assert_equal( 0x7FFFffffFFFFffff, float2nr( 1.0e150))
    call assert_equal(-0x7FFFffffFFFFffff, float2nr(-1.0e150))

    let rng = range(0xFFFFffff, 0x100000001)
    call assert_equal([0xFFFFffff, 0x100000000, 0x100000001], rng)
    call assert_equal(0x100000001, max(rng))
    call assert_equal(0xFFFFffff, min(rng))
    call assert_equal(rng, sort(range(0x100000001, 0xFFFFffff, -1), 'N'))
endfunc

"-------------------------------------------------------------------------------
" Test 95:  lines of :append, :change, :insert			    {{{1
"-------------------------------------------------------------------------------

func DefineFunction(name, body)
    let func = join(['function! ' . a:name . '()'] + a:body + ['endfunction'], "\n")
    exec func
endfunc

func Test_script_lines()
    " :append
    try
	call DefineFunction('T_Append', [
		    \ 'append',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Append', [
		    \ 'append',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry

    " :change
    try
	call DefineFunction('T_Change', [
		    \ 'change',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Change', [
		    \ 'change',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry

    " :insert
    try
	call DefineFunction('T_Insert', [
		    \ 'insert',
		    \ 'py <<EOS',
		    \ '.',
		    \ ])
    catch
	call assert_report("Can't define function")
    endtry
    try
	call DefineFunction('T_Insert', [
		    \ 'insert',
		    \ 'abc',
		    \ ])
	call assert_report("Shouldn't be able to define function")
    catch
	call assert_exception('Vim(function):E1145: Missing heredoc end marker: .')
    endtry
endfunc

"-------------------------------------------------------------------------------
" Test 96:  line continuation						    {{{1
"
"	    Undefined behavior was detected by ubsan with line continuation
"	    after an empty line.
"-------------------------------------------------------------------------------
func Test_script_empty_line_continuation()

    \
endfunc

"-------------------------------------------------------------------------------
" Test 97:  bitwise functions						    {{{1
"-------------------------------------------------------------------------------
func Test_bitwise_functions()
    " and
    call assert_equal(127, and(127, 127))
    call assert_equal(16, and(127, 16))
    eval 127->and(16)->assert_equal(16)
    call assert_equal(0, and(127, 128))
    call assert_fails("call and([], 1)", 'E745:')
    call assert_fails("call and({}, 1)", 'E728:')
    call assert_fails("call and(1.0, 1)", 'E805:')
    call assert_fails("call and(1, 1.0)", 'E805:')
    call assert_fails("call and(1, [])", 'E745:')
    call assert_fails("call and(1, {})", 'E728:')
    " or
    call assert_equal(23, or(16, 7))
    call assert_equal(15, or(8, 7))
    eval 8->or(7)->assert_equal(15)
    call assert_equal(123, or(0, 123))
    call assert_fails("call or([], 1)", 'E745:')
    call assert_fails("call or({}, 1)", 'E728:')
    call assert_fails("call or(1.0, 1)", 'E805:')
    call assert_fails("call or(1, 1.0)", 'E805:')
    call assert_fails("call or(1, [])", 'E745:')
    call assert_fails("call or(1, {})", 'E728:')
    " xor
    call assert_equal(0, xor(127, 127))
    call assert_equal(111, xor(127, 16))
    eval 127->xor(16)->assert_equal(111)
    call assert_equal(255, xor(127, 128))
    call assert_fails("call xor(1.0, 1)", 'E805:')
    call assert_fails("call xor(1, 1.0)", 'E805:')
    call assert_fails("call xor([], 1)", 'E745:')
    call assert_fails("call xor({}, 1)", 'E728:')
    call assert_fails("call xor(1, [])", 'E745:')
    call assert_fails("call xor(1, {})", 'E728:')
    " invert
    call assert_equal(65408, and(invert(127), 65535))
    eval 127->invert()->and(65535)->assert_equal(65408)
    call assert_equal(65519, and(invert(16), 65535))
    call assert_equal(65407, and(invert(128), 65535))
    call assert_fails("call invert(1.0)", 'E805:')
    call assert_fails("call invert([])", 'E745:')
    call assert_fails("call invert({})", 'E728:')
endfunc

" Test using bang after user command				    {{{1
func Test_user_command_with_bang()
    command -bang Nieuw let nieuw = 1
    Ni!
    call assert_equal(1, nieuw)
    unlet nieuw
    delcommand Nieuw
endfunc

func Test_script_expand_sfile()
  let lines =<< trim END
    func s:snr()
      return expand('<sfile>')
    endfunc
    let g:result = s:snr()
  END
  call writefile(lines, 'Xexpand', 'D')
  source Xexpand
  call assert_match('<SNR>\d\+_snr', g:result)
  source Xexpand
  call assert_match('<SNR>\d\+_snr', g:result)

  unlet g:result
endfunc

func Test_compound_assignment_operators()
    " Test for number
    let x = 1
    let x += 10
    call assert_equal(11, x)
    let x -= 5
    call assert_equal(6, x)
    let x *= 4
    call assert_equal(24, x)
    let x /= 3
    call assert_equal(8, x)
    let x %= 3
    call assert_equal(2, x)
    let x .= 'n'
    call assert_equal('2n', x)

    " Test special cases: division or modulus with 0.
    let x = 1
    let x /= 0
    call assert_equal(0x7FFFFFFFFFFFFFFF, x)

    let x = -1
    let x /= 0
    call assert_equal(-0x7FFFFFFFFFFFFFFF, x)

    let x = 0
    let x /= 0
    call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x)

    let x = 1
    let x %= 0
    call assert_equal(0, x)

    let x = -1
    let x %= 0
    call assert_equal(0, x)

    let x = 0
    let x %= 0
    call assert_equal(0, x)

    " Test for string
    let x = 'str'
    let x .= 'ing'
    call assert_equal('string', x)
    let x += 1
    call assert_equal(1, x)

    " Test for float
    let x -= 1.5
    call assert_equal(-0.5, x)
    let x = 0.5
    let x += 4.5
    call assert_equal(5.0, x)
    let x -= 1.5
    call assert_equal(3.5, x)
    let x *= 3.0
    call assert_equal(10.5, x)
    let x /= 2.5
    call assert_equal(4.2, x)
    call assert_fails('let x %= 0.5', 'E734:')
    call assert_fails('let x .= "f"', 'E734:')
    let x = !3.14
    call assert_equal(0.0, x)

    " integer and float operations
    let x = 1
    let x *= 2.1
    call assert_equal(2.1, x)
    let x = 1
    let x /= 0.25
    call assert_equal(4.0, x)
    let x = 1
    call assert_fails('let x %= 0.25', 'E734:')
    let x = 1
    call assert_fails('let x .= 0.25', 'E734:')
    let x = 1.0
    call assert_fails('let x += [1.1]', 'E734:')

    " Test for environment variable
    let $FOO = 1
    call assert_fails('let $FOO += 1', 'E734:')
    call assert_fails('let $FOO -= 1', 'E734:')
    call assert_fails('let $FOO *= 1', 'E734:')
    call assert_fails('let $FOO /= 1', 'E734:')
    call assert_fails('let $FOO %= 1', 'E734:')
    let $FOO .= 's'
    call assert_equal('1s', $FOO)
    unlet $FOO

    " Test for option variable (type: number)
    let &scrolljump = 1
    let &scrolljump += 5
    call assert_equal(6, &scrolljump)
    let &scrolljump -= 2
    call assert_equal(4, &scrolljump)
    let &scrolljump *= 3
    call assert_equal(12, &scrolljump)
    let &scrolljump /= 2
    call assert_equal(6, &scrolljump)
    let &scrolljump %= 5
    call assert_equal(1, &scrolljump)
    call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:'])
    set scrolljump&vim

    let &foldlevelstart = 2
    let &foldlevelstart -= 1
    call assert_equal(1, &foldlevelstart)
    let &foldlevelstart -= 1
    call assert_equal(0, &foldlevelstart)
    let &foldlevelstart = 2
    let &foldlevelstart -= 2
    call assert_equal(0, &foldlevelstart)

    " Test for register
    let @/ = 1
    call assert_fails('let @/ += 1', 'E734:')
    call assert_fails('let @/ -= 1', 'E734:')
    call assert_fails('let @/ *= 1', 'E734:')
    call assert_fails('let @/ /= 1', 'E734:')
    call assert_fails('let @/ %= 1', 'E734:')
    let @/ .= 's'
    call assert_equal('1s', @/)
    let @/ = ''
endfunc

func Test_unlet_env()
  let $TESTVAR = 'yes'
  call assert_equal('yes', $TESTVAR)
  call assert_fails('lockvar $TESTVAR', 'E940:')
  call assert_fails('unlockvar $TESTVAR', 'E940:')
  call assert_equal('yes', $TESTVAR)
  if 0
    unlet $TESTVAR
  endif
  call assert_equal('yes', $TESTVAR)
  unlet $TESTVAR
  call assert_equal('', $TESTVAR)
endfunc

func Test_refcount()
    " Immediate values
    call assert_equal(-1, test_refcount(1))
    call assert_equal(-1, test_refcount('s'))
    call assert_equal(-1, test_refcount(v:true))
    call assert_equal(0, test_refcount([]))
    call assert_equal(0, test_refcount({}))
    call assert_equal(0, test_refcount(0zff))
    call assert_equal(0, test_refcount({-> line('.')}))
    call assert_equal(-1, test_refcount(0.1))
    if has('job')
        call assert_equal(0, test_refcount(job_start([&shell, &shellcmdflag, 'echo .'])))
    endif

    " No refcount types
    let x = 1
    call assert_equal(-1, test_refcount(x))
    let x = 's'
    call assert_equal(-1, test_refcount(x))
    let x = v:true
    call assert_equal(-1, test_refcount(x))
    let x = 0.1
    call assert_equal(-1, test_refcount(x))

    " Check refcount
    let x = []
    call assert_equal(1, test_refcount(x))

    let x = {}
    call assert_equal(1, x->test_refcount())

    let x = 0zff
    call assert_equal(1, test_refcount(x))

    let X = {-> line('.')}
    call assert_equal(1, test_refcount(X))
    let Y = X
    call assert_equal(2, test_refcount(X))

    if has('job')
        let job = job_start([&shell, &shellcmdflag, 'echo .'])
        call assert_equal(1, test_refcount(job))
        call assert_equal(1, test_refcount(job_getchannel(job)))
        call assert_equal(1, test_refcount(job))
    endif

    " Function arguments, copying and unassigning
    func ExprCheck(x, i)
        let i = a:i + 1
        call assert_equal(i, test_refcount(a:x))
        let Y = a:x
        call assert_equal(i + 1, test_refcount(a:x))
        call assert_equal(test_refcount(a:x), test_refcount(Y))
        let Y = 0
        call assert_equal(i, test_refcount(a:x))
    endfunc
    call ExprCheck([], 0)
    call ExprCheck({}, 0)
    call ExprCheck(0zff, 0)
    call ExprCheck({-> line('.')}, 0)
    if has('job')
	call ExprCheck(job, 1)
	call ExprCheck(job_getchannel(job), 1)
	call job_stop(job)
    endif
    delfunc ExprCheck

    " Regarding function
    func Func(x) abort
        call assert_equal(2, test_refcount(function('Func')))
        call assert_equal(0, test_refcount(funcref('Func')))
    endfunc
    call assert_equal(1, test_refcount(function('Func')))
    call assert_equal(0, test_refcount(function('Func', [1])))
    call assert_equal(0, test_refcount(funcref('Func')))
    call assert_equal(0, test_refcount(funcref('Func', [1])))
    let X = function('Func')
    let Y = X
    call assert_equal(1, test_refcount(X))
    let X = function('Func', [1])
    let Y = X
    call assert_equal(2, test_refcount(X))
    let X = funcref('Func')
    let Y = X
    call assert_equal(2, test_refcount(X))
    let X = funcref('Func', [1])
    let Y = X
    call assert_equal(2, test_refcount(X))
    unlet X
    unlet Y
    call Func(1)
    delfunc Func

    " Function with dict
    func DictFunc() dict
        call assert_equal(3, test_refcount(self))
    endfunc
    let d = {'Func': function('DictFunc')}
    call assert_equal(1, test_refcount(d))
    call assert_equal(0, test_refcount(d.Func))
    call d.Func()
    unlet d
    delfunc DictFunc

    if has('channel')
      call assert_equal(-1, test_refcount(test_null_job()))
      call assert_equal(-1, test_refcount(test_null_channel()))
    endif
    call assert_equal(-1, test_refcount(test_null_function()))
    call assert_equal(-1, test_refcount(test_null_partial()))
    call assert_equal(-1, test_refcount(test_null_blob()))
    call assert_equal(-1, test_refcount(test_null_list()))
    call assert_equal(-1, test_refcount(test_null_dict()))
endfunc

" Test for missing :endif, :endfor, :endwhile and :endtry           {{{1
func Test_missing_end()
  call writefile(['if 2 > 1', 'echo ">"'], 'Xscript', 'D')
  call assert_fails('source Xscript', 'E171:')
  call writefile(['for i in range(5)', 'echo i'], 'Xscript')
  call assert_fails('source Xscript', 'E170:')
  call writefile(['while v:true', 'echo "."'], 'Xscript')
  call assert_fails('source Xscript', 'E170:')
  call writefile(['try', 'echo "."'], 'Xscript')
  call assert_fails('source Xscript', 'E600:')

  " Using endfor with :while
  let caught_e732 = 0
  try
    while v:true
    endfor
  catch /E732:/
    let caught_e732 = 1
  endtry
  call assert_equal(1, caught_e732)

  " Using endwhile with :for
  let caught_e733 = 0
  try
    for i in range(1)
    endwhile
  catch /E733:/
    let caught_e733 = 1
  endtry
  call assert_equal(1, caught_e733)

  " Using endfunc with :if
  call assert_fails('exe "if 1 | endfunc | endif"', 'E193:')

  " Missing 'in' in a :for statement
  call assert_fails('for i range(1) | endfor', 'E690:')

  " Incorrect number of variables in for
  call assert_fails('for [i,] in range(3) | endfor', 'E475:')
endfunc

" Test for deep nesting of if/for/while/try statements              {{{1
func Test_deep_nest()
  CheckRunVimInTerminal

  let lines =<< trim [SCRIPT]
    " Deep nesting of if ... endif
    func Test1()
      let @a = join(repeat(['if v:true'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endif'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of for ... endfor
    func Test2()
      let @a = join(repeat(['for i in [1]'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endfor'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of while ... endwhile
    func Test3()
      let @a = join(repeat(['while v:true'], 51), "\n")
      let @a ..= "\n"
      let @a ..= join(repeat(['endwhile'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of try ... endtry
    func Test4()
      let @a = join(repeat(['try'], 51), "\n")
      let @a ..= "\necho v:true\n"
      let @a ..= join(repeat(['endtry'], 51), "\n")
      @a
      let @a = ''
    endfunc

    " Deep nesting of function ... endfunction
    func Test5()
      let @a = join(repeat(['function X()'], 51), "\n")
      let @a ..= "\necho v:true\n"
      let @a ..= join(repeat(['endfunction'], 51), "\n")
      @a
      let @a = ''
    endfunc
  [SCRIPT]
  call writefile(lines, 'Xscript', 'D')

  let buf = RunVimInTerminal('-S Xscript', {'rows': 6})

  " Deep nesting of if ... endif
  call term_sendkeys(buf, ":call Test1()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})

  " Deep nesting of for ... endfor
  call term_sendkeys(buf, ":call Test2()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})

  " Deep nesting of while ... endwhile
  call term_sendkeys(buf, ":call Test3()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})

  " Deep nesting of try ... endtry
  call term_sendkeys(buf, ":call Test4()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})

  " Deep nesting of function ... endfunction
  call term_sendkeys(buf, ":call Test5()\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))})
  call term_sendkeys(buf, "\<C-C>\n")
  call TermWait(buf)

  "let l = ''
  "for i in range(1, 6)
  "  let l ..= term_getline(buf, i) . "\n"
  "endfor
  "call assert_report(l)

  call StopVimInTerminal(buf)
endfunc

" Test for errors in converting to float from various types         {{{1
func Test_float_conversion_errors()
  call assert_fails('let x = 4.0 % 2.0', 'E804:')
  call assert_fails('echo 1.1[0]', 'E806:')
  call assert_fails('echo sort([function("min"), 1], "f")', 'E891:')
  call assert_fails('echo 3.2 == "vim"', 'E892:')
  call assert_fails('echo sort([[], 1], "f")', 'E893:')
  call assert_fails('echo sort([{}, 1], "f")', 'E894:')
  call assert_fails('echo 3.2 == v:true', 'E362:')
  call assert_fails('echo 3.2 == v:none', 'E907:')
endfunc

" invalid function names               {{{1
func Test_invalid_function_names()
  " function name not starting with capital
  let caught_e128 = 0
  try
    func! g:test()
      echo "test"
    endfunc
  catch /E128:/
    let caught_e128 = 1
  endtry
  call assert_equal(1, caught_e128)

  " function name includes a colon
  let caught_e884 = 0
  try
    func! b:test()
      echo "test"
    endfunc
  catch /E884:/
    let caught_e884 = 1
  endtry
  call assert_equal(1, caught_e884)

  " function name followed by #
  let caught_e128 = 0
  try
    func! test2() "#
      echo "test2"
    endfunc
  catch /E128:/
    let caught_e128 = 1
  endtry
  call assert_equal(1, caught_e128)

  " function name starting with/without "g:", buffer-local funcref.
  function! g:Foo(n)
    return 'called Foo(' . a:n . ')'
  endfunction
  let b:my_func = function('Foo')
  call assert_equal('called Foo(1)', b:my_func(1))
  call assert_equal('called Foo(2)', g:Foo(2))
  call assert_equal('called Foo(3)', Foo(3))
  delfunc g:Foo

  " script-local function used in Funcref must exist.
  let lines =<< trim END
    func s:Testje()
      return "foo"
    endfunc
    let Bar = function('s:Testje')
    call assert_equal(0, exists('s:Testje'))
    call assert_equal(1, exists('*s:Testje'))
    call assert_equal(1, exists('Bar'))
    call assert_equal(1, exists('*Bar'))
  END
  call writefile(lines, 'Xscript', 'D')
  source Xscript
endfunc

" substring and variable name              {{{1
func Test_substring_var()
  let str = 'abcdef'
  let n = 3
  call assert_equal('def', str[n:])
  call assert_equal('abcd', str[:n])
  call assert_equal('d', str[n:n])
  unlet n
  let nn = 3
  call assert_equal('def', str[nn:])
  call assert_equal('abcd', str[:nn])
  call assert_equal('d', str[nn:nn])
  unlet nn
  let b:nn = 4
  call assert_equal('ef', str[b:nn:])
  call assert_equal('abcde', str[:b:nn])
  call assert_equal('e', str[b:nn:b:nn])
  unlet b:nn
endfunc

" Test using s: with a typed command              {{{1
func Test_typed_script_var()
  CheckRunVimInTerminal

  let buf = RunVimInTerminal('', {'rows': 6})

  " Deep nesting of if ... endif
  call term_sendkeys(buf, ":echo get(s:, 'foo', 'x')\n")
  call TermWait(buf)
  call WaitForAssert({-> assert_match('^E116:', term_getline(buf, 5))})

  call StopVimInTerminal(buf)
endfunc

" Test for issue6776              {{{1
func Test_ternary_expression()
  try
    call eval('0 ? 0')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))

  try
    call eval('0 ? "burp')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))

  try
    call eval('1 ? 0 : "burp')
  catch
  endtry
  " previous failure should not cause next expression to fail
  call assert_equal(v:false, eval(string(v:false)))
endfunction

func Test_for_over_string()
  let res = ''
  for c in 'aéc̀d'
    let res ..= c .. '-'
  endfor
  call assert_equal('a-é-c̀-d-', res)

  let res = ''
  for c in ''
    let res ..= c .. '-'
  endfor
  call assert_equal('', res)

  let res = ''
  for c in test_null_string()
    let res ..= c .. '-'
  endfor
  call assert_equal('', res)

  " Test for using "_" as the loop variable
  let i = 0
  let s = 'abc'
  for _ in s
    call assert_equal(s[i], _)
    let i += 1
  endfor
endfunc

" Test for 'for' loop failures
func Test_for_loop_failure()
  CheckFeature job
  func ForFn()
    for x in test_null_job()
    endfor
  endfunc
  call assert_fails('call ForFn()', 'E1523: String, List, Tuple or Blob required')
  delfunc ForFn
endfunc

" Test for deeply nested :source command  {{{1
func Test_deeply_nested_source()
  let lines =<< trim END

      so
      sil 0scr
      delete
      so
      0
  END
  call writefile(["vim9 silent! @0 \n/"] + lines, 'Xnested.vim', 'D')

  " this must not crash
  let cmd = GetVimCommand() .. " -e -s -S Xnested.vim -c qa!"
  call system(cmd)
endfunc

func Test_exception_silent()
  XpathINIT
  let lines =<< trim END
  func Throw()
    Xpath 'a'
    throw "Uncaught"
    " This line is not executed.
    Xpath 'b'
  endfunc
  " The exception is suppressed due to the presence of silent!.
  silent! call Throw()
  try
    call DoesNotExist()
  catch /E117:/
    Xpath 'c'
  endtry
  Xpath 'd'
  END
  let verify =<< trim END
    call assert_equal('acd', g:Xpath)
  END

  call RunInNewVim(lines, verify)
endfunc

"-------------------------------------------------------------------------------
" Modelines								    {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
"-------------------------------------------------------------------------------
