Skip to content

Commit cc660d1

Browse files
authored
Improvements to core parsing hooks (#12)
* Make ParseError work with Meta.parse() * Make incremental parsing of statements work * Add some basic tests
1 parent 20134c3 commit cc660d1

File tree

4 files changed

+50
-10
lines changed

4 files changed

+50
-10
lines changed

src/hooks.jl

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Adaptor for the API/ABI expected by the Julia runtime code.
2-
function core_parser_hook(code, filename, offset, options)
2+
function core_parser_hook(code, filename, lineno, offset, options)
33
try
44
# TODO: Check that we do all this input wrangling without copying the
55
# code buffer
@@ -13,19 +13,32 @@ function core_parser_hook(code, filename, offset, options)
1313

1414
stream = ParseStream(io)
1515
rule = options === :all ? :toplevel : options
16+
if rule !== :toplevel
17+
# To copy the flisp parser driver, we ignore leading trivia when
18+
# parsing statements or atoms
19+
bump_trivia(stream)
20+
end
1621
JuliaSyntax.parse(stream; rule=rule)
1722

18-
ex = if any_error(stream)
23+
if any_error(stream)
1924
e = Expr(:error, ParseError(SourceFile(code), stream.diagnostics))
20-
options === :all ? Expr(:toplevel, e) : e
25+
ex = options === :all ? Expr(:toplevel, e) : e
2126
else
22-
build_tree(Expr, stream)
27+
ex = build_tree(Expr, stream, wrap_toplevel_as_kind=K"None")
28+
if Meta.isexpr(ex, :None)
29+
# The None wrapping is only to give somewhere for trivia to be
30+
# attached; unwrap!
31+
ex = only(ex.args)
32+
end
2333
end
2434

25-
pos = last_byte(stream) - 1
35+
# Note the next byte in 1-based indexing is `last_byte(stream) + 1` but
36+
# the Core hook must return an offset (ie, it's 0-based) so the factors
37+
# of one cancel here.
38+
last_offset = last_byte(stream)
2639

2740
# Rewrap result in an svec for use by the C code
28-
return Core.svec(ex, pos)
41+
return Core.svec(ex, last_offset)
2942
catch exc
3043
@error("JuliaSyntax parser failed — falling back to flisp!",
3144
exception=(exc,catch_backtrace()),
@@ -35,6 +48,18 @@ function core_parser_hook(code, filename, offset, options)
3548
return Core.Compiler.fl_parse(code, filename, offset, options)
3649
end
3750

51+
# Core._parse gained a `lineno` argument in
52+
# https://github.com/JuliaLang/julia/pull/43876
53+
# Prior to this, the following signature was needed:
54+
function core_parser_hook(code, filename, offset, options)
55+
core_parser_hook(code, filename, LineNumberNode(0), offset, options)
56+
end
57+
58+
# Hack:
59+
# Meta.parse() attempts to construct a ParseError from a string if it receives
60+
# `Expr(:error)`.
61+
Base.Meta.ParseError(e::JuliaSyntax.ParseError) = e
62+
3863
"""
3964
Connect the JuliaSyntax parser to the Julia runtime so that it replaces the
4065
flisp parser for all parsing work.

src/parser.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2969,11 +2969,11 @@ function parse_brackets(after_parse::Function,
29692969
# (x \n\n for a in as) ==> (generator x (= a as))
29702970
parse_generator(ps, mark)
29712971
else
2972-
k_str = untokenize(k)
2973-
ck_str = untokenize(closing_kind)
29742972
if is_closing_token(ps, k)
2973+
k_str = untokenize(k, unique=false)
29752974
emit_diagnostic(ps, error="unexpected `$k_str` in bracketed list")
29762975
else
2976+
ck_str = untokenize(closing_kind)
29772977
emit_diagnostic(ps, error="missing comma or $ck_str in bracketed list")
29782978
end
29792979
# Recovery done after loop

test/hooks.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@testset "Hooks for Core integration" begin
2+
JuliaSyntax.enable_in_core!()
3+
4+
@test Meta.parse("x + 1") == :(x + 1)
5+
@test Meta.parse("x + 1", 1) == (:(x + 1), 6)
6+
7+
# Test that parsing statements incrementally works
8+
@test Meta.parse("x + 1\n(y)", 1) == (:(x + 1), 6)
9+
@test Meta.parse("x + 1\n(y)", 6) == (:y, 10)
10+
11+
# Check that Meta.parse throws the JuliaSyntax.ParseError rather than
12+
# Meta.ParseError when Core integration is enabled.
13+
@test_throws JuliaSyntax.ParseError Meta.parse("[x")
14+
15+
JuliaSyntax.disable_in_core!()
16+
end

test/runtests.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@ include("parse_stream.jl")
1919
include("parser.jl")
2020
include("parser_api.jl")
2121
include("syntax_tree.jl")
22-
2322
@testset "Parsing values from strings" begin
2423
include("value_parsing.jl")
2524
end
26-
25+
include("hooks.jl")
2726
include("parse_packages.jl")
2827

2928
# Prototypes

0 commit comments

Comments
 (0)