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

Add coercion of arguments to @wrap #991

Merged
merged 7 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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 Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ GAP_lib_jll = "~400.1300.000"
GAP_pkg_juliainterface_jll = "=0.900.000"
InteractiveUtils = "1.6"
Libdl = "1.6"
MacroTools = "0.5"
MacroTools = "0.5.13"
Markdown = "1.6"
Ncurses_jll = "6.4.1"
Pidfile = "1.3"
Expand Down
54 changes: 45 additions & 9 deletions src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ However, the generated function actually caches the GAP object `GAP.Globals.NAME
This minimizes the call overhead. So @wrap typically is used to provide an optimized
way to call certain GAP functions.

If an argument is annotated as `::GapObj`, the resulting function accepts arguments
of any type and wraps them in `GapObj(...)` before passing them to the GAP function.

Another use case for this macro is to improve type stability of code calling into
GAP, via the type annotations for the arguments and return value contained in the
function declaration.
Expand All @@ -343,6 +346,12 @@ Jacobi (generic function with 1 method)

julia> Jacobi(11,35)
1
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved

julia> GAP.@wrap IsString(x::GapObj)::Bool
IsString (generic function with 1 method)

julia> IsString("abc")
true
```
"""
macro wrap(ex)
Expand All @@ -359,23 +368,50 @@ macro wrap(ex)

fullargs = ex.args[2:length(ex.args)]

# strip type annotation from arguments for use in the call to GAP
args = [x isa Symbol ? x : x.args[1] for x in fullargs]

# splits the arguments with type annotations into expressions for the lhs and rhs
# of the call of the form `func(args) = GAP.Globals.func(args)` (see below)
tempargs = [
begin
if x isa Symbol
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
# no type annotations -> use x for lhs and rhs
(x, x)
elseif x.head == :(::) && length(x.args) == 2
# type annotations -> split and decide what to do
var = x.args[1]
lgoettgens marked this conversation as resolved.
Show resolved Hide resolved
typeannot = x.args[2]
if typeannot in [:GapObj, :(GAP.GapObj)]
# the lhs has no type annotation, rhs gets wrapped in `GAP.GapObj(...)::GAP.GapObj`
(var, :(GAP.GapObj($var)::GAP.GapObj))
elseif typeannot == :(GAP.Obj)
# the lhs has no type annotation, rhs gets wrapped in `GAP.Obj(...)::GAP.Obj`
(var, :(GAP.Obj($var)::GAP.Obj))
elseif typeannot in [:GapInt, :(GAP.GapInt)]
# the lhs has no type annotation, rhs gets wrapped in `GAP.GapInt(...)::GAP.GapInt`
(var, :(GAP.GapInt($var)::GAP.GapInt))
else
# remove type annotation on the rhs
(x, var)
end
else
error("unknown argument syntax around `$x`")
end
end for x in fullargs
]
lhsargs = map(first, tempargs)
rhsargs = map(last, tempargs)

# the "outer" part of the body
body = quote
body = MacroTools.@qq begin
global $newsym
if !isassigned($newsym)
$newsym[] = GAP.Globals.$name::GapObj
end
return $newsym[]($(args...))::$retval
return $newsym[]($(rhsargs...))::$retval
end

# insert the correct line number
body.args[1] = __source__

return esc(quote
return esc(MacroTools.@qq begin
@eval const $newsym = Ref{GapObj}()
Base.@__doc__ $ex = $body
Base.@__doc__ $(Expr(:call, name, lhsargs...)) = $body
end)
end
Loading