From 24907a9d8ec83644e0106925cff436b1c363fc04 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Tue, 11 Feb 2020 20:33:40 +0100 Subject: [PATCH 1/9] generalize to allow for different labels --- src/OptionalArgChecks.jl | 47 +++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/OptionalArgChecks.jl b/src/OptionalArgChecks.jl index 3f0e62b..2d2e921 100644 --- a/src/OptionalArgChecks.jl +++ b/src/OptionalArgChecks.jl @@ -3,7 +3,7 @@ module OptionalArgChecks using IRTools: @dynamo, IR, recurse!, block, branches, branch! using MacroTools: postwalk -export @argcheck, @skipargcheck +export @mark, @elide#, @skipargcheck """ @argcheck ex @@ -30,22 +30,38 @@ julia> @skipargcheck half(3) 1 ``` """ -macro argcheck(ex) - return Expr(:block, Expr(:meta, :begin_argcheck), esc(ex), Expr(:meta, :end_argcheck)) +macro mark(label, ex) + label isa Symbol || error("label has to be a Symbol") + return Expr( + :block, + Expr(:meta, :begin_optional, label), + esc(ex), + Expr(:meta, :end_optional, label), + ) end -@dynamo function skipargcheck(x...) +struct ElideCheck{label} + ElideCheck(label::Symbol) = new{label}() +end + +@dynamo function (::ElideCheck{label})(x...) where {label} ir = IR(x...) ir === nothing && return next = iterate(ir) while next !== nothing (x, st), state = next - if Meta.isexpr(st.expr, :meta) && st.expr.args[1] === :begin_argcheck + if Meta.isexpr(st.expr, :meta) && + st.expr.args[1] === :begin_optional && + st.expr.args[2] === label + orig = block(ir, x) delete!(ir, x) (x, st), state = iterate(ir, state) - while !(Meta.isexpr(st.expr, :meta) && st.expr.args[1] === :end_argcheck) + while !(Meta.isexpr(st.expr, :meta) && + st.expr.args[1] === :end_optional && + st.expr.args[2] === label) + delete!(ir, x) (x, st), state = iterate(ir, state) end @@ -63,6 +79,17 @@ end return ir end +macro elide(label, ex) + label isa Symbol || error("label has to be a Symbol") + ex = postwalk(ex) do x + if Meta.isexpr(x, :call) + pushfirst!(x.args, Expr(:call, GlobalRef(@__MODULE__, :ElideCheck), Expr(:quote, label))) + end + return x + end + return esc(ex) +end + """ @skipargcheck ex @@ -70,13 +97,7 @@ For every function call in `ex`, expressions wrapped in [`@argcheck`](@ref) get recursively. """ macro skipargcheck(ex) - ex = postwalk(ex) do x - if Meta.isexpr(x, :call) - pushfirst!(x.args, GlobalRef(@__MODULE__, :skipargcheck)) - end - return x - end - return esc(ex) + return :(@elide argcheck $(esc(ex))) end end From 6b88418541244627554cf993d43e7e33df8091ed Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Tue, 11 Feb 2020 20:37:21 +0100 Subject: [PATCH 2/9] fix tests and docstrings --- src/OptionalArgChecks.jl | 6 +++--- test/runtests.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OptionalArgChecks.jl b/src/OptionalArgChecks.jl index 2d2e921..c8d55fa 100644 --- a/src/OptionalArgChecks.jl +++ b/src/OptionalArgChecks.jl @@ -3,17 +3,17 @@ module OptionalArgChecks using IRTools: @dynamo, IR, recurse!, block, branches, branch! using MacroTools: postwalk -export @mark, @elide#, @skipargcheck +export @mark, @elide, @skipargcheck """ - @argcheck ex + @mark label ex Marks `ex` as an optional argument check, so when a function is called via [`@skipargcheck`](@ref), `ex` will be omitted. ```jldoctest julia> function half(x::Integer) - @argcheck iseven(x) || throw(DomainError(x, "x has to be an even number")) + @mark argcheck iseven(x) || throw(DomainError(x, "x has to be an even number")) return x ÷ 2 end half (generic function with 1 method) diff --git a/test/runtests.jl b/test/runtests.jl index 87ec382..121b44c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Test using OptionalArgChecks function f(x) - @argcheck x<2 && if x == 1 + @mark argcheck x<2 && if x == 1 return "foo" else error("Test") From 0d0aeda2e13f5c000e845ca9048ddba02f9f9e19 Mon Sep 17 00:00:00 2001 From: simeonschaub Date: Wed, 12 Feb 2020 13:53:06 +0100 Subject: [PATCH 3/9] reexport at-argcheck and at-check Co-Authored-By: Jan Weidner --- src/OptionalArgChecks.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/OptionalArgChecks.jl b/src/OptionalArgChecks.jl index c8d55fa..cca9c8a 100644 --- a/src/OptionalArgChecks.jl +++ b/src/OptionalArgChecks.jl @@ -5,6 +5,10 @@ using MacroTools: postwalk export @mark, @elide, @skipargcheck +# reexport @argcheck and @check +using ArgCheck: @argcheck, @check +export @argcheck, @check + """ @mark label ex @@ -83,7 +87,11 @@ macro elide(label, ex) label isa Symbol || error("label has to be a Symbol") ex = postwalk(ex) do x if Meta.isexpr(x, :call) - pushfirst!(x.args, Expr(:call, GlobalRef(@__MODULE__, :ElideCheck), Expr(:quote, label))) + pushfirst!(x.args, Expr( + :call, + GlobalRef(@__MODULE__, :ElideCheck), + Expr(:quote, label) + )) end return x end From 2b874441ae6baea779f378dc2c8ba15ddb4ca3e7 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:24:46 +0100 Subject: [PATCH 4/9] update and add docs --- docs/src/index.md | 8 ++++++-- src/OptionalArgChecks.jl | 16 +++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 0b572fc..6235afe 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -4,8 +4,12 @@ CurrentModule = OptionalArgChecks # OptionalArgChecks -Provides two macros, [`@argcheck`](@ref) and [`@skipargcheck`](@ref) which give users -control over whether to skip argument checking for better performance. +Provides two macros, [`@mark`](@ref) and [`@elide`](@ref) which give users control over +skipping arbitrary code in functions for better performance. + +For convenience, this package also exports [`@argcheck`](@ref) and [`@check`](@ref) from +the package [`ArgCheck.jl`](https://github.com/jw3126/ArgCheck.jl) and provides the macro +`@skipargcheck` to skip these checks. ## API diff --git a/src/OptionalArgChecks.jl b/src/OptionalArgChecks.jl index cca9c8a..0384ca9 100644 --- a/src/OptionalArgChecks.jl +++ b/src/OptionalArgChecks.jl @@ -13,11 +13,11 @@ export @argcheck, @check @mark label ex Marks `ex` as an optional argument check, so when a function is called via -[`@skipargcheck`](@ref), `ex` will be omitted. +[`@elide`](@ref) with label `label`, `ex` will be omitted. ```jldoctest julia> function half(x::Integer) - @mark argcheck iseven(x) || throw(DomainError(x, "x has to be an even number")) + @mark check_even iseven(x) || throw(DomainError(x, "x has to be an even number")) return x ÷ 2 end half (generic function with 1 method) @@ -30,7 +30,7 @@ ERROR: DomainError with 3: x has to be an even number [...] -julia> @skipargcheck half(3) +julia> @elide check_even half(3) 1 ``` """ @@ -83,6 +83,12 @@ end return ir end +""" + @elide label ex + +For every function call in `ex`, expressions marked with label `label` using the macro +[`@mark`](@ref) get omitted recursively. +""" macro elide(label, ex) label isa Symbol || error("label has to be a Symbol") ex = postwalk(ex) do x @@ -101,8 +107,8 @@ end """ @skipargcheck ex -For every function call in `ex`, expressions wrapped in [`@argcheck`](@ref) get omitted -recursively. +Elides argument checks created with [`@argcheck`](@ref) or [`@check`](@ref), provided by the +package `ArgCheck.jl`. Is equivalent to `@elide argcheck ex`. """ macro skipargcheck(ex) return :(@elide argcheck $(esc(ex))) From 586d5ebf9c4116fc749ecba57364c781b0e14ec8 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:33:27 +0100 Subject: [PATCH 5/9] add ArgCheck as dependency --- Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Project.toml b/Project.toml index 9a4d017..e85a786 100644 --- a/Project.toml +++ b/Project.toml @@ -4,12 +4,14 @@ authors = ["Simeon Schaub"] version = "0.1.0" [deps] +ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" IRTools = "7869d1d1-7146-5819-86e3-90919afe41df" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" [compat] IRTools = "0.3" MacroTools = "0.5" +ArgCheck = "1.1" julia = "1" [extras] From bd70c27f72c650781daad6240ea5b29382563c5d Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:33:54 +0100 Subject: [PATCH 6/9] add Test for @argcheck --- test/runtests.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 121b44c..eebe9f2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,11 @@ for x in 0:2 @test @skipargcheck f(x) == x + 3 end +g(x) = @argcheck x > 0 + +@test_throws ArgumentError g(-1) +@test g(1) === nothing + using Documenter DocMeta.setdocmeta!(OptionalArgChecks, :DocTestSetup, :(using OptionalArgChecks); recursive=true) doctest(OptionalArgChecks; manual = false) From a05ae5da0e35425e7ce2f7e228ea0cd7a35bb63c Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:38:31 +0100 Subject: [PATCH 7/9] enable PR preview --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index c3ba44b..a21135e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -20,4 +20,5 @@ makedocs(; deploydocs(; repo="github.com/simeonschaub/OptionalArgChecks.jl", + push_preview = true, ) From 9c0a379780d830fed3fbabe689ac16bef408fad3 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:38:47 +0100 Subject: [PATCH 8/9] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e85a786..93e6ec6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "OptionalArgChecks" uuid = "dfbeeb84-381a-44da-9ec9-a723abf299c7" authors = ["Simeon Schaub"] -version = "0.1.0" +version = "0.2.0" [deps] ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" From 43562335b029ad043ffb22004c7fdb24fe6885f4 Mon Sep 17 00:00:00 2001 From: Simeon Schaub Date: Wed, 12 Feb 2020 14:44:55 +0100 Subject: [PATCH 9/9] fix docs --- docs/src/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 6235afe..2f3d661 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,9 +7,10 @@ CurrentModule = OptionalArgChecks Provides two macros, [`@mark`](@ref) and [`@elide`](@ref) which give users control over skipping arbitrary code in functions for better performance. -For convenience, this package also exports [`@argcheck`](@ref) and [`@check`](@ref) from -the package [`ArgCheck.jl`](https://github.com/jw3126/ArgCheck.jl) and provides the macro -`@skipargcheck` to skip these checks. +For convenience, this package also exports [`@argcheck`](https://github.com/jw3126/ArgCheck.jl) +and [`@check`](https://github.com/jw3126/ArgCheck.jl) from the package +[`ArgCheck.jl`](https://github.com/jw3126/ArgCheck.jl) and provides the macro +[`@skipargcheck`](@ref) to skip these checks. ## API