1
- _debug_log = nothing
1
+ # This file provides an adaptor to match the API expected by the Julia runtime
2
+ # code in the binding Core._parse
3
+
4
+ # Use caller's world age.
5
+ const _caller_world = typemax (UInt)
6
+ const _parser_world_age = Ref {UInt} (_caller_world)
2
7
3
- # Adaptor for the API/ABI expected by the Julia runtime code.
4
8
function core_parser_hook (code, filename, lineno, offset, options)
9
+ # `hook` is always _core_parser_hook, but that's hidden from the compiler
10
+ # via a Ref to prevent invalidation / recompilation when other packages are
11
+ # loaded. This wouldn't seem like it should be necessary given the use of
12
+ # invoke_in_world, but it is in Julia-1.7.3. I'm not sure exactly which
13
+ # latency it's removing.
14
+ hook = _core_parser_hook_ref[]
15
+ if _parser_world_age[] != _caller_world
16
+ Base. invoke_in_world (_parser_world_age[], hook,
17
+ code, filename, lineno, offset, options)
18
+ else
19
+ hook (code, filename, lineno, offset, options)
20
+ end
21
+ end
22
+
23
+ # Core._parse gained a `lineno` argument in
24
+ # https://github.com/JuliaLang/julia/pull/43876
25
+ # Prior to this, the following signature was needed:
26
+ function core_parser_hook (code, filename, offset, options)
27
+ core_parser_hook (code, filename, LineNumberNode (0 ), offset, options)
28
+ end
29
+
30
+ # Debug log file for dumping parsed code
31
+ const _debug_log = Ref {Union{Nothing,IO}} (nothing )
32
+
33
+ function _core_parser_hook (code, filename, lineno, offset, options)
5
34
try
6
35
# TODO : Check that we do all this input wrangling without copying the
7
36
# code buffer
@@ -10,13 +39,13 @@ function core_parser_hook(code, filename, lineno, offset, options)
10
39
(ptr,len) = code
11
40
code = String (unsafe_wrap (Array, ptr, len))
12
41
end
13
- if ! isnothing (_debug_log)
14
- print (_debug_log, """
42
+ if ! isnothing (_debug_log[] )
43
+ print (_debug_log[] , """
15
44
#-#-#-------------------------------
16
45
# ENTER filename=$filename , lineno=$lineno , offset=$offset , options=$options "
17
46
#-#-#-------------------------------
18
47
""" )
19
- write (_debug_log, code)
48
+ write (_debug_log[] , code)
20
49
end
21
50
22
51
io = IOBuffer (code)
@@ -58,8 +87,8 @@ function core_parser_hook(code, filename, lineno, offset, options)
58
87
# of one cancel here.
59
88
last_offset = last_byte (stream)
60
89
61
- if ! isnothing (_debug_log)
62
- println (_debug_log, """
90
+ if ! isnothing (_debug_log[] )
91
+ println (_debug_log[] , """
63
92
#-#-#-
64
93
# EXIT last_offset=$last_offset
65
94
#-#-#-
@@ -69,42 +98,59 @@ function core_parser_hook(code, filename, lineno, offset, options)
69
98
# Rewrap result in an svec for use by the C code
70
99
return Core. svec (ex, last_offset)
71
100
catch exc
101
+ if ! isnothing (_debug_log[])
102
+ println (_debug_log[], """
103
+ #-#-#-
104
+ # ERROR EXIT
105
+ # $exc
106
+ #-#-#-
107
+ """ )
108
+ end
72
109
@error (" JuliaSyntax parser failed — falling back to flisp!" ,
73
110
exception= (exc,catch_backtrace ()),
74
111
offset= offset,
75
112
code= code)
76
- end
77
- return Core. Compiler. fl_parse (code, filename, offset, options)
78
- end
79
113
80
- # Core._parse gained a `lineno` argument in
81
- # https://github.com/JuliaLang/julia/pull/43876
82
- # Prior to this, the following signature was needed:
83
- function core_parser_hook (code, filename, offset, options)
84
- core_parser_hook (code, filename, LineNumberNode (0 ), offset, options)
114
+ if VERSION >= v " 1.8.0-DEV.1370" # https://github.com/JuliaLang/julia/pull/43876
115
+ return Core. Compiler. fl_parse (code, filename, lineno, offset, options)
116
+ else
117
+ return Core. Compiler. fl_parse (code, filename, offset, options)
118
+ end
119
+ end
85
120
end
86
121
87
122
# Hack:
88
123
# Meta.parse() attempts to construct a ParseError from a string if it receives
89
- # `Expr(:error)`.
124
+ # `Expr(:error)`. Add an override to the ParseError constructor to prevent this.
125
+ # FIXME : Improve this in Base somehow?
90
126
Base. Meta. ParseError (e:: JuliaSyntax.ParseError ) = e
91
127
92
128
const _default_parser = Core. _parse
129
+ # NB: Never reassigned, but the compiler doesn't know this!
130
+ const _core_parser_hook_ref = Ref {Function} (_core_parser_hook)
93
131
94
132
"""
133
+ enable_in_core!([enable=true; freeze_world_age, debug_filename])
134
+
95
135
Connect the JuliaSyntax parser to the Julia runtime so that it replaces the
96
- flisp parser for all parsing work.
136
+ flisp parser for all parsing work. That is, JuliaSyntax will be used for
137
+ `include()` `Meta.parse()`, the REPL, etc. To disable, set use
138
+ `enable_in_core!(false)`.
97
139
98
- That is, JuliaSyntax will be used for `include()` `Meta.parse()`, the REPL, etc.
140
+ Keyword arguments:
141
+ * `freeze_world_age` - Use a fixed world age for the parser to prevent
142
+ recompilation of the parser due to any user-defined methods (default `true`).
143
+ * `debug_filename` - File name of parser debug log (defaults to `nothing` or
144
+ the value of `ENV["JULIA_SYNTAX_DEBUG_FILE"]`).
99
145
"""
100
- function enable_in_core! (enable= true )
101
- debug_filename = get (ENV , " JULIA_SYNTAX_DEBUG_FILE" , nothing )
102
- global _debug_log
146
+ function enable_in_core! (enable= true ; freeze_world_age = true ,
147
+ debug_filename = get (ENV , " JULIA_SYNTAX_DEBUG_FILE" , nothing ) )
148
+ _parser_world_age[] = freeze_world_age ? Base . get_world_counter () : _caller_world
103
149
if enable && ! isnothing (debug_filename)
104
- _debug_log = open (debug_filename, " w" )
105
- elseif ! enable && ! isnothing (_debug_log)
106
- close (_debug_log)
107
- _debug_log = nothing
150
+ _debug_log[] = open (debug_filename, " w" )
151
+ elseif ! enable && ! isnothing (_debug_log[] )
152
+ close (_debug_log[] )
153
+ _debug_log[] = nothing
108
154
end
109
155
parser = enable ? core_parser_hook : _default_parser
110
156
Base. eval (Core, :(_parse = $ parser))
0 commit comments