/
exceptions.jl
167 lines (147 loc) · 5.34 KB
/
exceptions.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
struct GrammarError <: Exception
message::String
end
# These are concrete to allow messages to be attached
# to them.
struct ParseError <: Exception end
struct LexError <: Exception
message::String
end
struct UnexpectedEOF <: Exception
expected::Array
end
Base.showerror(io::IO, ue::UnexpectedEOF) = begin
println("Unexpected end-of-input. Expected one of:"* join(expected,"\t\t*")*"\n")
end
#==
The UnexpectedInput type holds the common behaviour of
the UnexpectedCharacters and UnexpectedToken types
==#
abstract type UnexpectedInput <: Exception end
# TODO: are we accessing strings in a Unicode-aware way?
get_context(ui::UnexpectedInput,text;span=40) = begin
pos = ui.pos_in_stream
start = max(pos - span,1)
_end = min(pos + span,length(text))
before = rsplit(text[start:pos],"\n",limit=2)[end]
after = split(text[pos+1:_end],"\n",limit=2)[1]
return before*after*"\n"*repeat(" ",length(before)-1) * "^\n"
end
"""
Given a parser instance and a dictionary mapping some label with
some malformed syntax examples, it'll return the label for the
example that bests matches the current error.
For Julia we need to make sure the correct parse_fn is dispatched for
this to make sense.
"""
match_examples(ui::UnexpectedInput,parse_call,examples::Dict,token_type_match_fallback=false,use_accepts=false) = begin
match_examples(ui,parse_call,((a,b) for (a,b) in examples),
token_type_match_fallback=token_type_match_fallback,
use_accepts=use_accepts)
end
match_examples(ui::UnexpectedInput,parse_call,examples,token_type_match_fallback=false,use_accepts=false) = begin
candidate = (nothing,false)
for (i,(label,example)) in enumerate(examples)
@assert !(example <: AbstractString)
for (j,malformed) in enumerate(example)
try
parse_call(malformed)
catch ut
if ut isa UnexpectedInput
if ut.state == ui.state
if use_accepts && ut.accepts != ui.accepts
@debug "Different accepts with the same state[$(ui.state)]: $(ui.accepts) != $(ut.accepts) at example [$i][$j]"
continue
end
try
if ut.token == ui.token
@debug "Exact match at example [$i][$j]"
return label
end
if token_type_match_fallback
if (ut.token.type_ == ui.token.type_) && candidate[end]==nothing
@debug "Token type fallback at example [$i][$j]"
candidate = label,true
end
end
catch a
if !(a isa AttributeError)
rethrow(a)
end
end
if candidate[1] == nothing
@debug "Same state match at example [$i][$j]"
candidate = label,false
end
end
else
rethrow(ut)
end
end
end
end
return candidate[1]
end
"""
UnexpectedCharacters
This exception is raised when the next characters in the input
stream cannot be matched with any acceptable tokens.
"""
struct UnexpectedCharacters <: UnexpectedInput
seq
pos_in_stream
line
column
allowed
considered_tokens
state
token_history
end
UnexpectedCharacters(seq,lex_pos,line,column;allowed=nothing,consider_tokens=nothing,state=nothing,token_history=nothing) = UnexpectedCharacters(seq,lex_pos,line,column,allowed,consider_tokens,state,token_history)
Base.showerror(io::IO,uc::UnexpectedCharacters) = begin
s = uc.seq[uc.pos_in_stream]
message = "No terminal defined for $s at line $(uc.line) col $(uc.column)"
message = message * "\n\n" * get_context(uc,uc.seq)
if uc.allowed != nothing
message *= "\nExpecting: $(uc.allowed)"
end
if uc.token_history != nothing
message *= "\nPrevious tokens: " * join(uc.token_history,", ")*"\n"
end
println(io,message)
end
"""
UnexpectedToken
This exception is raised when a token is encountered in the input stream
that is not allowed by the grammar.
"""
struct UnexpectedToken <: UnexpectedInput
token
expected
line
column
considered_rules
state
pos_in_stream
puppet
accepts
end
UnexpectedToken(token,expected;considered_rules=nothing,state=nothing,puppet=nothing) = begin
line = token.line
column = token.column
pos_in_stream = token.pos_in_stream
accepts = puppet != nothing && accepts(puppet)
UnexpectedToken(token,expected,line,column,considered_rules,state,pos_in_stream,puppet,accepts)
end
Base.showerror(io::IO,ut::UnexpectedToken) = begin
message = "Unexpected token $(ut.token) at line $(ut.line), column $(ut.column).\n"
message *= "Expected one of: $(join(ut.expected,", "))"
println(message)
end
struct VisitError <: Exception
tree
orig_exc
end
Base.showerror(io::IO,ve::VisitError) = begin
println("Error trying to process rule \"$(ve.tree.data):\n\n$(ve.orig_exc)\" ")
end