Skip to content

Commit

Permalink
🚧 Start fixing @method macro tests..
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Sep 11, 2024
1 parent 4161a17 commit 68c77d2
Show file tree
Hide file tree
Showing 7 changed files with 387 additions and 276 deletions.
5 changes: 3 additions & 2 deletions src/Framework/macro_helpers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ function to_value(mod, expression, context, error_out, type = nothing)
ev = quote
val = $ev
val isa $type || $error_out(
"$($context): expression does not evaluate to a $($type): \
$($(repr(expression))), but to a $(typeof(val)): $val.",
"$($context): expression does not evaluate to a '$($type)':\n\
Expression: $($(repr(expression)))\n\
Result: $val ::$(typeof(val))",
)
val
end
Expand Down
41 changes: 17 additions & 24 deletions src/Framework/method_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#
# f(v::Value, ...) = <invoker code>
#
# If no explicit receiver is found,
# then first argument is assumed to be the receiver provided it is untyped or `::Any`.
#
# Then, the macro invokation goes like:
#
# @method f depends(components...) read_as(names...) # or write_as(names...)
Expand Down Expand Up @@ -96,7 +93,7 @@ function method_macro(__module__, __source__, input...)
# unless there is another programmatic way to add a method to a function in Julia?
xp = input[1]
fn_xp, ValueType_xp = nothing, nothing # (hepl JuliaLS)
@capture(xp, fn_xp_{ValueTypeXp_} | fn_xp_)
@capture(xp, fn_xp_{ValueType_xp_} | fn_xp_)
fn_xp isa Symbol ||
fn_xp isa Expr && fn_xp.head == :. ||
perr("Not a method identifier or path: $(repr(fn_xp)).")
Expand All @@ -105,7 +102,7 @@ function method_macro(__module__, __source__, input...)
tovalue(ValueType_xp, "System value type", Type)
push_res!(quote

ValueType = $ValueType_xp # Explicit or uninferred.
ValueType = $ValueType_xp # Explicit or inferred.
fn = $(tovalue(fn_xp, "System method", Function))

end)
Expand Down Expand Up @@ -135,11 +132,11 @@ function method_macro(__module__, __source__, input...)
ValueType = system_value_type(C)
else
if !(C <: Component{ValueType})
actual_V = system_value_type(C)
C_V = system_value_type(C)
xerr("Depends section: system value type
is supposed to be '$ValueType' \
based on the first macro argument, \
but '$C' subtypes '$(Component{actual_V})' \
but '$C' subtypes '$(Component{C_V})' \
and not '$(Component{ValueType})'.")
end
end
Expand All @@ -161,11 +158,11 @@ function method_macro(__module__, __source__, input...)
if Base.isidentifier(kw)
if !(kw in (read_kw, write_kw))
perr("Invalid section keyword: $(repr(kw)). \
Expected `$read_kw` or `$write_kw` or `depends`.")
Expected :$read_kw or :$write_kw or :depends.")
end
if !isnothing(proptype)
proptype == kw && perr("The `$kw` section is specified twice.")
perr("Cannot specify both `$proptype` section and `$kw`.")
proptype == kw && perr("The :$kw section is specified twice.")
perr("Cannot specify both :$proptype section and :$kw.")
end
for pname in propnames
pname isa Symbol ||
Expand All @@ -179,7 +176,7 @@ function method_macro(__module__, __source__, input...)

perr("Unexpected @method section. \
Expected `depends(..)`, `$read_kw(..)` or `$write_kw(..)`. \
Got: $(repr(i)).")
Got instead: $(repr(i)).")

end

Expand All @@ -191,7 +188,7 @@ function method_macro(__module__, __source__, input...)
push_res!(
quote
isnothing(ValueType) && xerr("The system value type cannot be inferred \
when no dependencies are given.
when no dependencies are given.\n\
Consider making it explicit \
with the first macro argument: \
`$fn{MyValueType}`.")
Expand Down Expand Up @@ -224,8 +221,9 @@ function method_macro(__module__, __source__, input...)
values = Set()
system_values = Set()
systems_only = Set()
for (p, n) in zip(parms, names)
for (i, (p, n)) in enumerate(zip(parms, names))
p isa Core.TypeofVararg && continue
n == Symbol("#unused#") && (n = Symbol('#', i))
p <: ValueType && push!(values, n)
p <: System{ValueType} && push!(system_values, n)
p === System && push!(systems_only, n)
Expand All @@ -234,15 +232,11 @@ function method_macro(__module__, __source__, input...)
(what, set, type) ->
xerr("Receiving several (possibly different) $what \
is not yet supported by the framework. \
Here both :$(pop!(set)) and :$(pop!(set)) \
Here both parameters :$(pop!(set)) and :$(pop!(set)) \
are of type $type.")
length(values) > 1 && xerr("system/values parameters", values, ValueType)
receiver = if isempty(values)
parms[1] === Any || continue
names[1]
else
pop!(values)
end
length(values) > 1 && severr("system/values parameters", values, ValueType)
isempty(values) && continue
receiver = pop!(values)
sv = system_values
length(sv) > 1 && severr("system hooks", sv, System{ValueType})
hook = if isempty(system_values)
Expand All @@ -259,8 +253,7 @@ function method_macro(__module__, __source__, input...)
isempty(to_wrap) &&
xerr("No suitable method has been found to mark $fn as a system method. \
Valid methods must have at least \
one 'receiver' argument of type ::$ValueType \
or a first ::Any argument to be implicitly considered as such.")
one 'receiver' argument of type ::$ValueType.")
end,
)

Expand Down Expand Up @@ -379,7 +372,7 @@ function method_macro(__module__, __source__, input...)
# when the macro is called within @testset blocks
# with LOCAL_MACROCALLS = true:
# https://stackoverflow.com/a/55292662/3719101)
$dep = missing_dependency_for($($efn), $receiver)
$dep = first_missing_dependency_for($($efn), $receiver)
if !isnothing($dep)
$a = isabstracttype($dep) ? " a" : ""
throw(
Expand Down
9 changes: 5 additions & 4 deletions src/Framework/methods.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# - method!(v::Value, rhs)
#
# .. can optionally become properties of the system/value,
# in the sense of julia's `getproperty/set_property`.
# in the sense of julia's `getproperty/set_property!`.
#
# The properties also come in two styles:
#
Expand All @@ -33,7 +33,7 @@ missing_dependencies_for(fn::Type{<:Function}, s::System{V}) where {V} =
!has_component(s, dep)
end
# Just pick the first one. Return nothing if dependencies are met.
function missing_dependency_for(fn::Type{<:Function}, s::System)
function first_missing_dependency_for(fn::Type{<:Function}, s::System)
for dep in missing_dependencies_for(fn, s)
return dep
end
Expand All @@ -43,7 +43,8 @@ end
# Direct call with the functions themselves.
depends(::Type{V}, fn::Function) where {V} = depends(V, typeof(fn))
missing_dependencies_for(fn::Function, s::System) = missing_dependencies_for(typeof(fn), s)
missing_dependency_for(fn::Function, s::System) = missing_dependency_for(typeof(fn), s)
first_missing_dependency_for(fn::Function, s::System) =
first_missing_dependency_for(typeof(fn), s)

# Map wrapped system value and property name to the corresponding function.
read_property(V::Type, ::Val{name}) where {name} =
Expand Down Expand Up @@ -137,6 +138,6 @@ struct PropertyError{V} <: SystemException
PropertyError(::Type{V}, s, m) where {V} = new{V}(s, m, PhantomData{V}())
end
function Base.showerror(io::IO, e::PropertyError{V}) where {V}
println(io, "In property '$(e.name)' of '$V': $(e.message)")
println(io, "In property :$(e.name) of '$V': $(e.message)")
end
properr(V, n, m) = throw(PropertyError(V, n, m))
8 changes: 4 additions & 4 deletions src/Framework/system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function Base.getproperty(system::System{V}, p::Symbol) where {V}
# Search property method.
fn = read_property(V, Val(p))
# Check for required components availability.
miss = missing_dependency_for(fn, system)
miss = first_missing_dependency_for(fn, system)
if !isnothing(miss)
comp = isabstracttype(miss) ? "A component $miss" : "Component $miss"
properr(V, p, "$comp is required to read this property.")
Expand All @@ -87,7 +87,7 @@ function Base.setproperty!(system::System{V}, p::Symbol, rhs) where {V}
# Search property method.
fn = readwrite_property(V, Val(p))
# Check for required components availability.
miss = missing_dependency_for(fn, system)
miss = first_missing_dependency_for(fn, system)
if !isnothing(miss)
comp = isabstracttype(miss) ? "A component $miss" : "Component $miss"
properr(V, p, "$comp is required to write to this property.")
Expand Down Expand Up @@ -166,7 +166,7 @@ export properties
# Yields (:propname, read, Option{write})
function properties(s::System{V}) where {V}
imap(ifilter(properties(V)) do (_, read, _, _)
isnothing(missing_dependency_for(read, s))
isnothing(first_missing_dependency_for(read, s))
end) do (name, read, write, _)
(name, read, write)
end
Expand All @@ -177,7 +177,7 @@ end
# Yields (:propname, read, Option{write}, iterator{missing_dependencies...})
function latent_properties(s::System{V}) where {V}
imap(ifilter(properties(V)) do (_, read, _, _)
!isnothing(missing_dependency_for(read, s))
!isnothing(first_missing_dependency_for(read, s))
end) do (name, read, write, deps)
(name, read, write, ifilter(d -> !has_component(s, d), deps))
end
Expand Down
4 changes: 2 additions & 2 deletions test/framework/04-conflicts_macro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ for letter in 'A':'Z'
end)
end

@testset "Invalid @conflicts macro invocations." begin
@testset "Declaring components @conflicts." begin

#---------------------------------------------------------------------------------------
# Provide enough data for the declaration to be meaningful.
Expand Down Expand Up @@ -275,7 +275,7 @@ using Test
const S = System{Value}
comps(s) = collect(components(s))

@testset "Abstract component types conflicts." begin
@testset "Abstract component conflicts semantics." begin

# Component type hierachy.
#
Expand Down
Loading

0 comments on commit 68c77d2

Please sign in to comment.