Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Curried getproperty syntax #53946

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ macro __doc__(x)
return Expr(:escape, Expr(:block, Expr(:meta, :doc), x))
end

isbasicdoc(@nospecialize x) = (isa(x, Expr) && x.head === :.) || isa(x, Union{QuoteNode, Symbol})
isbasicdoc(@nospecialize x) = (isa(x, Expr) && x.head === :var".") || isa(x, Union{QuoteNode, Symbol})
iscallexpr(ex::Expr) = (isa(ex, Expr) && ex.head === :where) ? iscallexpr(ex.args[1]) : (isa(ex, Expr) && ex.head === :call)
iscallexpr(ex) = false
function ignoredoc(source, mod, str, expr)
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ __dot__(x) = x
function __dot__(x::Expr)
dotargs = Base.mapany(__dot__, x.args)
if x.head === :call && dottable(x.args[1])
Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...))
Expr(:var".", dotargs[1], Expr(:tuple, dotargs[2:end]...))
elseif x.head === :comparison
Expr(:comparison, (iseven(i) && dottable(arg) && arg isa Symbol && isoperator(arg) ?
Symbol('.', arg) : arg for (i, arg) in pairs(dotargs))...)
Expand Down
6 changes: 3 additions & 3 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ namify(@nospecialize x) = astname(x, isexpr(x, :macro))::Union{Symbol,Expr,Globa

function astname(x::Expr, ismacro::Bool)
head = x.head
if head === :.
if head === :var"."
ismacro ? macroname(x) : x
elseif head === :call && isexpr(x.args[1], :(::))
return astname((x.args[1]::Expr).args[end], ismacro)
Expand All @@ -305,7 +305,7 @@ astname(@nospecialize(other), ismacro::Bool) = other
macroname(s::Symbol) = Symbol('@', s)
macroname(x::Expr) = Expr(x.head, x.args[1], macroname(x.args[end].value))

isfield(@nospecialize x) = isexpr(x, :.) &&
isfield(@nospecialize x) = isexpr(x, :var".") &&
(isa(x.args[1], Symbol) || isfield(x.args[1])) &&
(isa(x.args[2], QuoteNode) || isexpr(x.args[2], :quote))

Expand Down Expand Up @@ -559,7 +559,7 @@ isquotedmacrocall(@nospecialize x) =
isa(x.args[1], QuoteNode) &&
isexpr(x.args[1].value, :macrocall, 2)
# Simple expressions / atoms the may be documented.
isbasicdoc(@nospecialize x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol})
isbasicdoc(@nospecialize x) = isexpr(x, :var".") || isa(x, Union{QuoteNode, Symbol})
is_signature(@nospecialize x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where)

function docm(source::LineNumberNode, mod::Module, ex)
Expand Down
2 changes: 1 addition & 1 deletion base/docs/bindings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ resolve(b::Binding) = getfield(b.mod, b.var)

function splitexpr(x::Expr)
isexpr(x, :macrocall) ? splitexpr(x.args[1]) :
isexpr(x, :.) ? (x.args[1], x.args[2]) :
isexpr(x, :var".") ? (x.args[1], x.args[2]) :
error("Invalid @var syntax `$x`.")
end
splitexpr(s::Symbol) = Expr(:macrocall, getfield(Base, Symbol("@__MODULE__")), nothing), quot(s)
Expand Down
14 changes: 14 additions & 0 deletions base/experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,18 @@ adding them to the global method table.
"""
:@MethodTable

struct GetPropertyLens
syms::Tuple{Vararg{Symbol}}
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we use Fix1{getproperty} for this instead of defining a new type, if getproperty is extended to take a tuple of symbols as the second argument?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can. The original idea here was to have something more first class, but you can always turn Fix1{getproperty} into that by evaluating it on an appropriate tracing object. Since that's a more minimal change, I'll use that instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am somewhat surprised we can const-prop that reliably enough (esp to something like broadcast which tries not to inline this much) without putting the symbols in the type parameters of the Lens object. Should we put it there instead?

Base.getproperty() = GetPropertyLens(())
Base.getproperty(lens::GetPropertyLens, s::Symbol) =
GetPropertyLens(tuple(getfield(lens, :syms)..., s))

function (lens::GetPropertyLens)(strct)
syms = getfield(lens, :syms)
isempty(syms) && return strct
sym = first(syms)
return GetPropertyLens(Base.tail(syms))(Base.getproperty(strct, sym))
end

end
4 changes: 2 additions & 2 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1175,14 +1175,14 @@ end
function make_atomic(order, ex)
@nospecialize
if ex isa Expr
if isexpr(ex, :., 2)
if isexpr(ex, :var".", 2)
l, r = esc(ex.args[1]), esc(ex.args[2])
return :(getproperty($l, $r, $order))
elseif isexpr(ex, :call, 3)
return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
elseif ex.head === :(=)
l, r = ex.args[1], esc(ex.args[2])
if is_expr(l, :., 2)
if is_expr(l, :var".", 2)
ll, lr = esc(l.args[1]), esc(l.args[2])
return :(setproperty!($ll, $lr, $r, $order))
end
Expand Down
2 changes: 1 addition & 1 deletion base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1478,4 +1478,4 @@ julia> [1, 2] .∉ ([2, 3],)
0
```
"""
∉, ∌
∉, ∌
4 changes: 2 additions & 2 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2761,15 +2761,15 @@ function destructure_callex(topmod::Module, @nospecialize(ex))
push!(args, x)
end
end
elseif isexpr(ex, :.) # `x.f`
elseif isexpr(ex, :var".") # `x.f`
f = GlobalRef(topmod, :getproperty)
args = flatten(ex.args)
elseif isexpr(ex, :ref) # `x[i]`
f = GlobalRef(topmod, :getindex)
args = flatten(ex.args)
elseif isexpr(ex, :(=)) # `x.f = v` or `x[i] = v`
lhs, rhs = ex.args
if isexpr(lhs, :.)
if isexpr(lhs, :var".")
f = GlobalRef(topmod, :setproperty!)
args = flatten(Any[lhs.args..., rhs])
elseif isexpr(lhs, :ref)
Expand Down
28 changes: 14 additions & 14 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1516,7 +1516,7 @@ const expr_infix_wide = Set{Symbol}([
const expr_infix = Set{Symbol}([:(:), :(->), :(::)])
const expr_infix_any = union(expr_infix, expr_infix_wide)
const expr_calls = Dict(:call => ('(',')'), :calldecl => ('(',')'),
:ref => ('[',']'), :curly => ('{','}'), :(.) => ('(',')'))
:ref => ('[',']'), :curly => ('{','}'), :var"." => ('(',')'))
const expr_parens = Dict(:tuple=>('(',')'), :vcat=>('[',']'),
:hcat =>('[',']'), :row =>('[',']'), :vect=>('[',']'),
:ncat =>('[',']'), :nrow =>('[',']'),
Expand Down Expand Up @@ -1769,17 +1769,17 @@ end
# kw: `=` expressions are parsed with head `kw` in this context
function show_call(io::IO, head, func, func_args, indent, quote_level, kw::Bool)
op, cl = expr_calls[head]
if (isa(func, Symbol) && func !== :(:) && !(head === :. && isoperator(func))) ||
if (isa(func, Symbol) && func !== :(:) && !(head === :var"." && isoperator(func))) ||
(isa(func, Symbol) && !is_valid_identifier(func)) ||
(isa(func, Expr) && (func.head === :. || func.head === :curly || func.head === :macroname)) ||
(isa(func, Expr) && (func.head === :var"." || func.head === :curly || func.head === :macroname)) ||
isa(func, GlobalRef)
show_unquoted(io, func, indent, 0, quote_level)
else
print(io, '(')
show_unquoted(io, func, indent, 0, quote_level)
print(io, ')')
end
if head === :(.)
if head === :var"."
print(io, '.')
end
if !isempty(func_args) && isa(func_args[1], Expr) && (func_args[1]::Expr).head === :parameters
Expand Down Expand Up @@ -1907,7 +1907,7 @@ function valid_import_path(@nospecialize(ex), allow_as = true)
if allow_as && is_expr(ex, :as) && length((ex::Expr).args) == 2
ex = (ex::Expr).args[1]
end
return is_expr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args)
return is_expr(ex, :var".") && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args)
end

function show_import_path(io::IO, ex, quote_level)
Expand All @@ -1922,10 +1922,10 @@ function show_import_path(io::IO, ex, quote_level)
end
show_import_path(io, ex.args[i], quote_level)
end
elseif ex.head === :(.)
elseif ex.head === :var"."
for i = 1:length(ex.args)
sym = ex.args[i]::Symbol
if sym === :(.)
if sym === :var"."
print(io, '.')
else
if sym === :(..)
Expand All @@ -1946,7 +1946,7 @@ end
function allow_macroname(ex)
if (ex isa Symbol && first(string(ex)) == '@') ||
ex isa GlobalRef ||
(is_expr(ex, :(.)) && length(ex.args) == 2 &&
(is_expr(ex, :var".") && length(ex.args) == 2 &&
(is_expr(ex.args[2], :quote) || ex.args[2] isa QuoteNode))
return Expr(:macroname, ex)
else
Expand Down Expand Up @@ -1978,15 +1978,15 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
head, args, nargs = ex.head, ex.args, length(ex.args)
unhandled = false
# dot (i.e. "x.y"), but not compact broadcast exps
if head === :(.) && (nargs != 2 || !is_expr(args[2], :tuple))
if head === :var"." && (nargs != 2 || !is_expr(args[2], :tuple))
# standalone .op
if nargs == 1 && args[1] isa Symbol && isoperator(args[1]::Symbol)
print(io, "(.", args[1], ")")
elseif nargs == 2 && is_quoted(args[2])
item = args[1]
# field
field = unquoted(args[2])
parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !is_expr(item, :(.))
parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !is_expr(item, :var".")
parens && print(io, '(')
show_unquoted(io, item, indent, 0, quote_level)
parens && print(io, ')')
Expand Down Expand Up @@ -2155,7 +2155,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In

# other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)")
elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.)
funcargslike = head === :(.) ? (args[2]::Expr).args : args[2:end]
funcargslike = head === :var"." ? (args[2]::Expr).args : args[2:end]
show_call(head === :ref ? IOContext(io, beginsym=>true) : io, head, args[1], funcargslike, indent, quote_level, head !== :curly)

# comprehensions
Expand Down Expand Up @@ -2334,10 +2334,10 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
show_sym(io, arg1, allow_macroname=true)
elseif arg1 isa GlobalRef
show_globalref(io, arg1, allow_macroname=true)
elseif is_expr(arg1, :(.)) && length((arg1::Expr).args) == 2
elseif is_expr(arg1, :var".") && length((arg1::Expr).args) == 2
arg1 = arg1::Expr
m = arg1.args[1]
if m isa Symbol || m isa GlobalRef || is_expr(m, :(.), 2)
if m isa Symbol || m isa GlobalRef || is_expr(m, :var".", 2)
show_unquoted(io, m)
else
print(io, "(")
Expand Down Expand Up @@ -2754,7 +2754,7 @@ resolvebinding(@nospecialize(ex)) = ex
resolvebinding(ex::QuoteNode) = ex.value
resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex))
function resolvebinding(ex::Expr)
if ex.head === :. && isa(ex.args[2], Symbol)
if ex.head === :var"." && isa(ex.args[2], Symbol)
parent = resolvebinding(ex.args[1])
if isa(parent, Module)
return resolvebinding(GlobalRef(parent, ex.args[2]))
Expand Down
8 changes: 4 additions & 4 deletions src/jlfrontend.scm
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
`(incomplete ,msg)
(cons 'error (cdr e))))
(begin
;;(newline)
;;(display "unexpected error: ")
;;(prn e)
;;(print-stack-trace (stacktrace))
(newline)
(display "unexpected error: ")
(prn e)
(print-stack-trace (stacktrace))
'(error "malformed expression"))))
thk))

Expand Down
Loading