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

Support docstrings in @funsql notation #63

Merged
merged 3 commits into from
Jun 14, 2024
Merged
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
34 changes: 18 additions & 16 deletions docs/src/test/nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,29 +242,15 @@ A parameter of a query function accepts a type declaration.
end
=#

The `@funsql` macro applied to a constant definition transliterates the value.

@funsql const ip_or_er_visit_q = concept_by_code("Visit", "IP", "ER")

display(ip_or_er_visit_q)
#=>
let q1 = From(:concept),
q2 = q1 |>
Where(Fun.and(Fun."="(Get.vocabulary_id, "Visit"),
Fun.in(Get.concept_code, "IP", "ER")))
q2
end
=#

A single `@funsql` macro can wrap multiple definitions.

@funsql begin
SNOMED(codes...) = concept_by_code("SNOMED", $(codes...))

const myocardial_infarction_q = SNOMED("22298006")
`MYOCARDIAL INFARCTION`() = SNOMED("22298006")
end

display(myocardial_infarction_q)
display(@funsql `MYOCARDIAL INFARCTION`())
#=>
let q1 = From(:concept),
q2 = q1 |>
Expand All @@ -274,6 +260,22 @@ A single `@funsql` macro can wrap multiple definitions.
end
=#

A query function may have a docstring.

@funsql begin
"SNOMED concept set with the given `codes`"
SNOMED

"Visit concept set with the given `codes`"
Visit(codes...) = concept_by_code("Visit", $(codes...))
end

@doc funsql_SNOMED
#-> SNOMED concept set with the given `codes`

@doc funsql_Visit
#-> Visit concept set with the given `codes`

An ill-formed `@funsql` query triggers an error.

@funsql for p in person; end
Expand Down
23 changes: 6 additions & 17 deletions src/dissect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ function dissect(scr::Symbol, @nospecialize(pat))
ex1 = dissect(scr, pat.args[1])
ex2 = dissect(scr, pat.args[2])
:($ex2 && $ex1)
elseif pat.head === :(::) && nargs == 1
:($scr isa $(pat.args[1]))
elseif pat.head === :(::) && nargs == 2
ex1 = dissect(scr, pat.args[1])
ex2 = :($scr isa $(pat.args[2]))
Expand Down Expand Up @@ -110,22 +112,9 @@ function dissect(scr::Symbol, ::Type{QuoteNode}, pats::Vector{Any})
error("invalid pattern: $(repr(pats))")
end

function dissect(scr::Symbol, ::Type{Cmd}, pats::Vector{Any})
if length(pats) == 1
scr_ref = gensym(:scr_ref)
scr_str = gensym(:scr_str)
return quote
$scr isa Expr &&
$scr.head === :macrocall &&
length($scr.args) >= 2 &&
(local $scr_ref = $scr.args[1];
$scr_ref isa GlobalRef &&
$scr_ref.mod === Core &&
$scr_ref.name === $(QuoteNode(Symbol("@cmd")))) &&
(local $scr_str = $scr.args[end];
$scr_str isa String) &&
$(dissect(scr_str, pats[1]))
end
function dissect(scr::Symbol, ::Type{GlobalRef}, pats::Vector{Any})
if length(pats) == 2
return :($scr isa GlobalRef && $scr.mod == $(pats[1]) && $scr.name === $(pats[2]))
end
error("invalid pattern $(repr(pats))")
error("invalid pattern: $(repr(pats))")
end
20 changes: 13 additions & 7 deletions src/nodes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,16 @@ function transliterate(@nospecialize(ex), ctx::TransliterateContext)
if @dissect(ex, Expr(:($), arg))
# $(...)
return esc(arg)
elseif @dissect(ex, Expr(:(=), Expr(:call, name::Symbol, args...), body))
elseif @dissect(ex, Expr(:macrocall, ref := GlobalRef(Core, Symbol("@doc")), ln::LineNumberNode, doc, arg))
# "..." ...
if @dissect(arg, name::Symbol || Expr(:macrocall, GlobalRef(Core, Symbol("@cmd")), ::LineNumberNode, name::String))
arg = Symbol("funsql_$name")
else
ctx = TransliterateContext(ctx, src = ln)
arg = transliterate(arg, ctx)
end
return Expr(:macrocall, ref, ln, doc, arg)
elseif @dissect(ex, Expr(:(=), Expr(:call, name::Symbol || Expr(:macrocall, GlobalRef(Core, Symbol("@cmd")), ::LineNumberNode, name::String), args...), body))
# name(args...) = body
ctx = TransliterateContext(ctx, decl = true)
trs = Any[transliterate(arg, ctx) for arg in args]
Expand All @@ -538,9 +547,7 @@ function transliterate(@nospecialize(ex), ctx::TransliterateContext)
elseif @dissect(ex, Expr(:parameters, args...))
# ; args...
return Expr(:parameters, Any[transliterate(arg, ctx) for arg in args]...)
elseif @dissect(ex, Expr(op := :const || :global || :local, arg))
return Expr(op, transliterate(arg, ctx))
elseif @dissect(ex, Cmd(name))
elseif @dissect(ex, Expr(:macrocall, GlobalRef(Core, Symbol("@cmd")), ::LineNumberNode, name::String))
# `name`
return QuoteNode(Symbol(name))
elseif @dissect(ex, Expr(:call, Expr(:., over, QuoteNode(name)), args...))
Expand Down Expand Up @@ -619,14 +626,13 @@ function transliterate(@nospecialize(ex), ctx::TransliterateContext)
# name(args...)
trs = Any[transliterate(arg, ctx) for arg in args]
return :($(esc(Symbol("funsql_$name")))($(trs...)))
elseif @dissect(ex, Expr(:call, Cmd(name), args...))
elseif @dissect(ex, Expr(:call, Expr(:macrocall, GlobalRef(Core, Symbol("@cmd")), ::LineNumberNode, name::String), args...))
# `name`(args...)
trs = Any[transliterate(arg, ctx) for arg in args]
return :($(esc(Symbol("funsql_$name")))($(trs...)))
elseif @dissect(ex, Expr(:block, args...))
# begin; args...; end
if all(arg isa LineNumberNode ||
@dissect(arg, Expr(:(=) || :const || :global || :local, _...))
if all(@dissect(arg, ::LineNumberNode || Expr(:(=), _...) || Expr(:macrocall, GlobalRef(Core, Symbol("@doc")), _...))
for arg in args)
trs = Any[]
for arg in args
Expand Down
Loading