Skip to content

Commit

Permalink
move parse(::String, ...) to Meta module (#24455)
Browse files Browse the repository at this point in the history
move syntax tests to new file syntax.jl
  • Loading branch information
KristofferC authored and JeffBezanson committed Nov 6, 2017
1 parent 28dc9d3 commit a6fa576
Show file tree
Hide file tree
Showing 21 changed files with 1,327 additions and 1,315 deletions.
4 changes: 2 additions & 2 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,12 @@ function syntax_deprecation_warnings(f::Function, warn::Bool)
end

function parse_input_line(s::String; filename::String="none")
# (expr, pos) = parse(s, 1)
# (expr, pos) = Meta.parse(s, 1)
# (ex, pos) = ccall(:jl_parse_string, Any,
# (Ptr{UInt8},Csize_t,Int32,Int32),
# s, sizeof(s), pos-1, 1)
# if ex!==()
# throw(ParseError("extra input after end of expression"))
# throw(Meta.ParseError("extra input after end of expression"))
# end
# expr
ex = ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t),
Expand Down
5 changes: 5 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,11 @@ end
# issue #24167
@deprecate EnvHash EnvDict

# issue #24349
@deprecate parse(str::AbstractString; kwargs...) Meta.parse(str; kwargs...)
@deprecate parse(str::AbstractString, pos::Int, ; kwargs...) Meta.parse(str, pos; kwargs...)
@deprecate_binding ParseError Meta.ParseError

# #24258
# Physical units define an equivalence class: there is no such thing as a step of "1" (is
# it one day or one second or one nanosecond?). So require the user to specify the step
Expand Down
2 changes: 1 addition & 1 deletion base/docs/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function helpmode(io::IO, line::AbstractString)
Symbol(line)
else
x = Base.syntax_deprecation_warnings(false) do
parse(line, raise = false)
Meta.parse(line, raise = false)
end
# Retrieving docs for macros requires us to make a distinction between the text
# `@macroname` and `@macroname()`. These both parse the same, but are used by
Expand Down
6 changes: 3 additions & 3 deletions base/markdown/Julia/interp.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

function Base.parse(stream::IO; greedy::Bool = true, raise::Bool = true)
function _parse(stream::IO; greedy::Bool = true, raise::Bool = true)
pos = position(stream)
ex, Δ = Base.parse(read(stream, String), 1, greedy = greedy, raise = raise)
ex, Δ = Meta.parse(read(stream, String), 1, greedy = greedy, raise = raise)
seek(stream, pos + Δ - 1)
return ex
end
Expand All @@ -11,7 +11,7 @@ function interpinner(stream::IO, greedy = false)
startswith(stream, '$') || return
(eof(stream) || Char(peek(stream)) in whitespace) && return
try
return Base.parse(stream::IOBuffer, greedy = greedy)
return _parse(stream::IOBuffer, greedy = greedy)
catch e
return
end
Expand Down
85 changes: 85 additions & 0 deletions base/meta.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,89 @@ macro lower(mod, code)
return :(lower($(esc(mod)), $(QuoteNode(code))))
end


## interface to parser ##

"""
ParseError(msg)
The expression passed to the `parse` function could not be interpreted as a valid Julia
expression.
"""
struct ParseError <: Exception
msg::AbstractString
end

"""
parse(str, start; greedy=true, raise=true)
Parse the expression string and return an expression (which could later be passed to eval
for execution). `start` is the index of the first character to start parsing. If `greedy` is
`true` (default), `parse` will try to consume as much input as it can; otherwise, it will
stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically
valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true`
(default), syntax errors other than incomplete expressions will raise an error. If `raise`
is `false`, `parse` will return an expression that will raise an error upon evaluation.
```jldoctest
julia> Meta.parse("x = 3, y = 5", 7)
(:(y = 5), 13)
julia> Meta.parse("x = 3, y = 5", 5)
(:((3, y) = 5), 13)
```
"""
function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true)
# pos is one based byte offset.
# returns (expr, end_pos). expr is () in case of parse error.
bstr = String(str)
ex, pos = ccall(:jl_parse_string, Any,
(Ptr{UInt8}, Csize_t, Int32, Int32),
bstr, sizeof(bstr), pos-1, greedy ? 1 : 0)
if raise && isa(ex,Expr) && ex.head === :error
throw(ParseError(ex.args[1]))
end
if ex === ()
raise && throw(ParseError("end of input"))
ex = Expr(:error, "end of input")
end
return ex, pos+1 # C is zero-based, Julia is 1-based
end

"""
parse(str; raise=true)
Parse the expression string greedily, returning a single expression. An error is thrown if
there are additional characters after the first expression. If `raise` is `true` (default),
syntax errors will raise an error; otherwise, `parse` will return an expression that will
raise an error upon evaluation.
```jldoctest
julia> Meta.parse("x = 3")
:(x = 3)
julia> Meta.parse("x = ")
:($(Expr(:incomplete, "incomplete: premature end of input")))
julia> Meta.parse("1.0.2")
ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"")
Stacktrace:
[...]
julia> Meta.parse("1.0.2"; raise = false)
:($(Expr(:error, "invalid numeric constant \"1.0.\"")))
```
"""
function parse(str::AbstractString; raise::Bool=true)
ex, pos = parse(str, 1, greedy=true, raise=raise)
if isa(ex,Expr) && ex.head === :error
return ex
end
if !done(str, pos)
raise && throw(ParseError("extra token after end of expression"))
return Expr(:error, "extra token after end of expression")
end
return ex
end

end # module
83 changes: 0 additions & 83 deletions base/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -236,86 +236,3 @@ function parse(::Type{T}, s::AbstractString) where T<:AbstractFloat
return unsafe_get(result)
end

## interface to parser ##

"""
ParseError(msg)
The expression passed to the `parse` function could not be interpreted as a valid Julia
expression.
"""
struct ParseError <: Exception
msg::AbstractString
end

"""
parse(str, start; greedy=true, raise=true)
Parse the expression string and return an expression (which could later be passed to eval
for execution). `start` is the index of the first character to start parsing. If `greedy` is
`true` (default), `parse` will try to consume as much input as it can; otherwise, it will
stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically
valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true`
(default), syntax errors other than incomplete expressions will raise an error. If `raise`
is `false`, `parse` will return an expression that will raise an error upon evaluation.
```jldoctest
julia> parse("x = 3, y = 5", 7)
(:(y = 5), 13)
julia> parse("x = 3, y = 5", 5)
(:((3, y) = 5), 13)
```
"""
function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true)
# pos is one based byte offset.
# returns (expr, end_pos). expr is () in case of parse error.
bstr = String(str)
ex, pos = ccall(:jl_parse_string, Any,
(Ptr{UInt8}, Csize_t, Int32, Int32),
bstr, sizeof(bstr), pos-1, greedy ? 1 : 0)
if raise && isa(ex,Expr) && ex.head === :error
throw(ParseError(ex.args[1]))
end
if ex === ()
raise && throw(ParseError("end of input"))
ex = Expr(:error, "end of input")
end
return ex, pos+1 # C is zero-based, Julia is 1-based
end

"""
parse(str; raise=true)
Parse the expression string greedily, returning a single expression. An error is thrown if
there are additional characters after the first expression. If `raise` is `true` (default),
syntax errors will raise an error; otherwise, `parse` will return an expression that will
raise an error upon evaluation.
```jldoctest
julia> parse("x = 3")
:(x = 3)
julia> parse("x = ")
:($(Expr(:incomplete, "incomplete: premature end of input")))
julia> parse("1.0.2")
ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"")
Stacktrace:
[...]
julia> parse("1.0.2"; raise = false)
:($(Expr(:error, "invalid numeric constant \"1.0.\"")))
```
"""
function parse(str::AbstractString; raise::Bool=true)
ex, pos = parse(str, 1, greedy=true, raise=raise)
if isa(ex,Expr) && ex.head === :error
return ex
end
if !done(str, pos)
raise && throw(ParseError("extra token after end of expression"))
return Expr(:error, "extra token after end of expression")
end
return ex
end
2 changes: 1 addition & 1 deletion base/repl/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ function setup_interface(
end
end
ast, pos = Base.syntax_deprecation_warnings(false) do
Base.parse(input, oldpos, raise=false)
Meta.parse(input, oldpos, raise=false)
end
if (isa(ast, Expr) && (ast.head == :error || ast.head == :continue || ast.head == :incomplete)) ||
(done(input, pos) && !endswith(input, '\n'))
Expand Down
6 changes: 3 additions & 3 deletions base/repl/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function complete_symbol(sym, ffunc)
lookup_name, name = rsplit(sym, ".", limit=2)

ex = Base.syntax_deprecation_warnings(false) do
parse(lookup_name, raise=false)
Meta.parse(lookup_name, raise=false)
end

b, found = get_value(ex, context_module)
Expand Down Expand Up @@ -479,7 +479,7 @@ function completions(string, pos)
# First parse everything up to the current position
partial = string[1:pos]
inc_tag = Base.syntax_deprecation_warnings(false) do
Base.incomplete_tag(parse(partial, raise=false))
Base.incomplete_tag(Meta.parse(partial, raise=false))
end

# if completing a key in a Dict
Expand Down Expand Up @@ -527,7 +527,7 @@ function completions(string, pos)
if inc_tag == :other && should_method_complete(partial)
frange, method_name_end = find_start_brace(partial)
ex = Base.syntax_deprecation_warnings(false) do
parse(partial[frange] * ")", raise=false)
Meta.parse(partial[frange] * ")", raise=false)
end
if isa(ex, Expr) && ex.head==:call
return complete_methods(ex), start(frange):method_name_end, false
Expand Down
2 changes: 1 addition & 1 deletion base/shell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function shell_parse(str::AbstractString, interpolate::Bool=true;
error("space not allowed right after \$")
end
stpos = j
ex, j = parse(s,j,greedy=false)
ex, j = Meta.parse(s,j,greedy=false)
last_parse = stpos:j
update_arg(ex); i = j
else
Expand Down
8 changes: 4 additions & 4 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,8 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0)
# show_unquoted(io, ex) does the heavy lifting
#
# AST printing should follow two rules:
# 1. parse(string(ex)) == ex
# 2. eval(parse(repr(ex))) == ex
# 1. Meta.parse(string(ex)) == ex
# 2. eval(Meta.parse(repr(ex))) == ex
#
# Rule 1 means that printing an expression should generate Julia code which
# could be reparsed to obtain the original expression. This code should be
Expand All @@ -521,8 +521,8 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0)
# original expression.
#
# This is consistent with many other show methods, i.e.:
# show(Set([1,2,3])) # ==> "Set{Int64}([2,3,1])"
# eval(parse("Set{Int64}([2,3,1])”) # ==> An actual set
# show(Set([1,2,3])) # ==> "Set{Int64}([2,3,1])"
# eval(Meta.parse("Set{Int64}([2,3,1])”) # ==> An actual set
# While this isn’t true of ALL show methods, it is of all ASTs.

const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode,
Expand Down
14 changes: 7 additions & 7 deletions doc/src/manual/metaprogramming.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The next step is to [parse](https://en.wikipedia.org/wiki/Parsing#Computer_langu
into an object called an expression, represented by the Julia type `Expr`:

```jldoctest prog
julia> ex1 = parse(prog)
julia> ex1 = Meta.parse(prog)
:(1 + 1)
julia> typeof(ex1)
Expand Down Expand Up @@ -86,7 +86,7 @@ Expr
`Expr` objects may also be nested:

```jldoctest ex3
julia> ex3 = parse("(4 + 4) / 2")
julia> ex3 = Meta.parse("(4 + 4) / 2")
:((4 + 4) / 2)
```

Expand Down Expand Up @@ -160,12 +160,12 @@ Expr
(to view the structure of this expression, try `ex.head` and `ex.args`, or use [`dump`](@ref)
as above or [`Meta.@dump`](@ref))

Note that equivalent expressions may be constructed using [`parse`](@ref) or the direct `Expr`
Note that equivalent expressions may be constructed using [`Meta.parse`](@ref) or the direct `Expr`
form:

```jldoctest
julia> :(a + b*c + 1) ==
parse("a + b*c + 1") ==
julia> :(a + b*c + 1) ==
Meta.parse("a + b*c + 1") ==
Expr(:call, :+, :a, Expr(:call, :*, :b, :c), 1)
true
```
Expand Down Expand Up @@ -314,7 +314,7 @@ equivalent of `eval(eval(:x))`.
The usual representation of a `quote` form in an AST is an `Expr` with head `:quote`:

```jldoctest interp1
julia> dump(parse(":(1+2)"))
julia> dump(Meta.parse(":(1+2)"))
Expr
head: Symbol quote
args: Array{Any}((1,))
Expand All @@ -335,7 +335,7 @@ as an object of type `QuoteNode`.
The parser yields `QuoteNode`s for simple quoted items like symbols:

```jldoctest interp1
julia> dump(parse(":x"))
julia> dump(Meta.parse(":x"))
QuoteNode
value: Symbol x
```
Expand Down
4 changes: 2 additions & 2 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,6 @@ Base.@gensym
Base.@goto
Base.@label
Base.@polly
Base.parse(::AbstractString, ::Int)
Base.parse(::AbstractString)
```

## Nullables
Expand Down Expand Up @@ -343,6 +341,8 @@ Base.gc
Base.gc_enable
Meta.lower
Meta.@lower
Meta.parse(::AbstractString, ::Int)
Meta.parse(::AbstractString)
Base.macroexpand
Base.@macroexpand
Base.@macroexpand1
Expand Down
2 changes: 1 addition & 1 deletion test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function choosetests(choices = [])
"checked", "bitset", "floatfuncs", "compile", "distributed", "inline",
"boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "osutils",
"channels", "iostream", "specificity", "codegen", "codevalidation",
"reinterpretarray"
"reinterpretarray", "syntax"
]

if isdir(joinpath(JULIA_HOME, Base.DOCDIR, "examples"))
Expand Down
8 changes: 4 additions & 4 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -924,15 +924,15 @@ let x = Binding(curmod, :bindingdoesnotexist)
end

let x = Binding(Main, :+)
@test parse(string(x)) == :(Base.:+)
@test Meta.parse(string(x)) == :(Base.:+)
end

let x = Binding(Base, :parse)
@test parse(string(x)) == :(Base.parse)
let x = Binding(Meta, :parse)
@test Meta.parse(string(x)) == :(Base.Meta.parse)
end

let x = Binding(Main, :)
@test parse(string(x)) == :()
@test Meta.parse(string(x)) == :()
end

doc_util_path = Symbol(joinpath("docs", "utils.jl"))
Expand Down
Loading

0 comments on commit a6fa576

Please sign in to comment.