4
4
const _has_v1_6_hooks = VERSION >= v " 1.6"
5
5
const _has_v1_10_hooks = isdefined (Core, :_setparser! )
6
6
7
+ struct ErrorSpec
8
+ child_idx:: Int
9
+ node:: RedTreeCursor
10
+ parent_kind:: Kind
11
+ end
12
+
13
+ function first_error_cursor (stream:: ParseStream )
14
+ output = stream. output
15
+ for i = 2 : length (output)
16
+ is_error (output[i]) && return GreenTreeCursor (output, i)
17
+ end
18
+ end
19
+
7
20
# Find the first error in a SyntaxNode tree, returning the index of the error
8
21
# within its parent and the node itself.
9
- function _first_error (t:: SyntaxNode )
10
- if is_error (t)
11
- return 0 ,t
22
+ function first_tree_error (c:: RedTreeCursor , error_cursor:: GreenTreeCursor )
23
+ @assert ! is_leaf (c) && ! is_error (c)
24
+ first_child = first_error = nothing
25
+ it = reverse_nontrivia_children (c)
26
+ r = iterate (it)
27
+ local child
28
+ while r != = nothing
29
+ (child, state) = r
30
+ r = iterate (it, state)
31
+ (error_cursor in child || error_cursor == child. green) || continue
32
+ is_error (child) && break
33
+ return first_tree_error (child, error_cursor)
12
34
end
13
- if ! is_leaf (t)
14
- for (i,c) in enumerate (children (t))
15
- if is_error (c)
16
- return i,c
17
- else
18
- x = _first_error (c)
19
- if x != (0 ,nothing )
20
- return x
21
- end
22
- end
23
- end
35
+ i = 1 # count node index
36
+ while r != = nothing
37
+ i += 1
38
+ (_, state) = r
39
+ r = iterate (it, state)
40
+ end
41
+ return ErrorSpec (i, child, kind (c))
42
+ end
43
+
44
+ function first_tree_error (stream:: ParseStream )
45
+ c = RedTreeCursor (stream)
46
+ err = first_error_cursor (stream)
47
+ for c in reverse_toplevel_siblings (c)
48
+ is_error (c) && return ErrorSpec (0 , c, K " wrapper" )
49
+ is_leaf (c) && continue
50
+ return first_tree_error (c, err)
24
51
end
25
- return 0 ,nothing
26
52
end
27
53
28
54
# Classify an incomplete expression, returning a Symbol compatible with
32
58
# next if the incomplete stream was to continue. (Though this is just rough. In
33
59
# practice several categories are combined for the purposes of the REPL -
34
60
# perhaps we can/should do something more precise in the future.)
35
- function _incomplete_tag (n:: SyntaxNode , codelen)
36
- i,c = _first_error (n)
61
+ function _incomplete_tag (theerror:: ErrorSpec , codelen)
62
+ i = theerror. child_idx
63
+ c = theerror. node
64
+ kp = theerror. parent_kind
37
65
if isnothing (c) || last_byte (c) < codelen || codelen == 0
38
66
if kind (c) == K " ErrorEofMultiComment"
39
67
# This is the one weird case where the token itself is an
@@ -47,18 +75,16 @@ function _incomplete_tag(n::SyntaxNode, codelen)
47
75
# here as a hard error.
48
76
return :none
49
77
end
50
- if kind (c) == K " error" && numchildren (c) > 0
51
- for cc in children (c)
78
+ if kind (c) == K " error" && is_non_terminal (c)
79
+ for cc in reverse_nontrivia_children (c)
52
80
if kind (cc) == K " error"
53
81
return :other
54
82
end
55
83
end
56
84
end
57
- if isnothing (c . parent)
85
+ if kp == K " wrapper "
58
86
return :other
59
- end
60
- kp = kind (c. parent)
61
- if kp == K " string" || kp == K " var"
87
+ elseif kp == K " string" || kp == K " var"
62
88
return :string
63
89
elseif kp == K " cmdstring"
64
90
return :cmd
@@ -181,8 +207,8 @@ function core_parser_hook(code, filename::String, lineno::Int, offset::Int, opti
181
207
182
208
if any_error (stream)
183
209
pos_before_comments = last_non_whitespace_byte (stream)
184
- tree = build_tree (SyntaxNode, stream, first_line = lineno, filename = filename )
185
- tag = _incomplete_tag (tree , pos_before_comments)
210
+ errspec = first_tree_error ( stream)
211
+ tag = _incomplete_tag (errspec , pos_before_comments)
186
212
if _has_v1_10_hooks
187
213
exc = ParseError (stream, filename= filename, first_line= lineno,
188
214
incomplete_tag= tag)
@@ -211,15 +237,15 @@ function core_parser_hook(code, filename::String, lineno::Int, offset::Int, opti
211
237
# * truncates the top level expression arg list before that error
212
238
# * includes the last line number
213
239
# * appends the error message
214
- topex = Expr (tree)
240
+ source = SourceFile (stream, filename= filename, first_line= lineno)
241
+ topex = build_tree (Expr, stream, source)
215
242
@assert topex. head == :toplevel
216
243
i = findfirst (_has_nested_error, topex. args)
217
244
if i > 1 && topex. args[i- 1 ] isa LineNumberNode
218
245
i -= 1
219
246
end
220
247
resize! (topex. args, i- 1 )
221
- _,errort = _first_error (tree)
222
- push! (topex. args, LineNumberNode (source_line (errort), filename))
248
+ push! (topex. args, LineNumberNode (source_line (source, first_byte (errspec. node)), filename))
223
249
push! (topex. args, error_ex)
224
250
topex
225
251
else
402
428
# Convenience functions to mirror `JuliaSyntax.parsestmt(Expr, ...)` in simple cases.
403
429
fl_parse (:: Type{Expr} , args... ; kws... ) = fl_parse (args... ; kws... )
404
430
fl_parseall (:: Type{Expr} , args... ; kws... ) = fl_parseall (args... ; kws... )
405
-
0 commit comments