Skip to content

Instantly share code, notes, and snippets.

@c42f
Last active September 23, 2019 07:37

Revisions

  1. c42f revised this gist Sep 22, 2019. 1 changed file with 20 additions and 5 deletions.
    25 changes: 20 additions & 5 deletions exc_lowering.jl
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ isquoted(ex) = ex isa QuoteNode || isexpr(ex, (:quote, :meta))
    function has_throws(ex)
    if ex isa Expr
    if (ex.head == :call && ex.args[1] == :throw) ||
    (ex.head == :macrocall && ex.args[1] == Symbol("@throws"))
    (ex.head == :macrocall && ex.args[1] == Symbol("@th"))
    return true
    elseif !isquoted(ex)
    return any(has_throws, ex.args)
    @@ -43,11 +43,12 @@ function expand_throws_body!(ex)
    end
    end
    end
    elseif ex.head == :macrocall && ex.args[1] == Symbol("@throws")
    # `@throws ex1 exc_type` is a standin for "real syntax" like
    elseif ex.head == :macrocall && ex.args[1] == Symbol("@th")
    # `@th ex1 throws exc_type` is a standin for "real syntax" like
    # `ex1 throws exc_type`
    ex1 = ex.args[3]
    exc_type = ex.args[4]
    @assert ex.args[4] == :throws
    exc_type = ex.args[5]
    res = gensym("res")
    matcher = gensym("exc_matcher")
    ex = quote
    @@ -165,7 +166,7 @@ end

    @throws function foo(x::Int)
    if x > 1
    y = @throws bar() Exception
    y = @th bar() throws Exception
    else
    y = 2
    end
    @@ -179,6 +180,16 @@ end
    # @inline foo(_match_expected_errors_1, s::String) = foo(s)


    # New try form:
    #
    # x = 10
    # try
    # z = foo(x) throws Exception
    # @show z
    # catch exc::ErrorException
    # @show exc
    # end

    x = 2
    # catch
    _match_expected_errors_0(e) = e isa ErrorException ? ErrorResult(e) : e
    @@ -194,6 +205,10 @@ else
    @show exc
    end

    # @try foo() ErrorException=>1


    # Normal try
    try
    z = foo(x)
    @show z
  2. c42f revised this gist Sep 21, 2019. 1 changed file with 119 additions and 14 deletions.
    133 changes: 119 additions & 14 deletions exc_lowering.jl
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,102 @@
    module Asdf
    using Base.Meta

    struct ErrorResult{E}
    e::E
    end

    function insert_wraparg!(ex, name)
    @assert isexpr(ex, :call) # todo
    insert!(ex.args, 2, name)
    ex
    end

    isquoted(ex) = ex isa QuoteNode || isexpr(ex, (:quote, :meta))

    function has_throws(ex)
    if ex isa Expr
    if (ex.head == :call && ex.args[1] == :throw) ||
    (ex.head == :macrocall && ex.args[1] == Symbol("@throws"))
    return true
    elseif !isquoted(ex)
    return any(has_throws, ex.args)
    end
    else
    return false
    end
    end

    function expand_throws_body!(ex)
    if ex isa Expr
    if ex.head == :call && ex.args[1] == :throw
    # `throw(e)` is implicitly
    # `throw(e) throws Exception`
    exc = gensym("exc")
    mexc = gensym("matched_exc")
    ex = quote
    $exc = $(ex.args[2])
    if $exc isa Exception
    $mexc = __exception_matcher__($exc)
    if $mexc isa $ErrorResult
    return $mexc
    else
    throw($exc)
    end
    end
    end
    elseif ex.head == :macrocall && ex.args[1] == Symbol("@throws")
    # `@throws ex1 exc_type` is a standin for "real syntax" like
    # `ex1 throws exc_type`
    ex1 = ex.args[3]
    exc_type = ex.args[4]
    res = gensym("res")
    matcher = gensym("exc_matcher")
    ex = quote
    $matcher(e) = e isa $exc_type ? __exception_matcher__(e) : e
    $res = $(insert_wraparg!(ex1, matcher))
    if $res isa $ErrorResult # TODO (top ErrorResult) ?
    return $res
    end
    $res
    end
    elseif !isquoted(ex)
    map!(expand_throws_body!, ex.args, ex.args)
    end
    end
    ex
    end

    function argname(ex)
    if ex isa Symbol
    return ex
    end
    if isexpr(ex, :(::))
    argname(ex.args[1])
    elseif isexpr(ex, :(=))
    argname(ex.args[1])
    else
    error("invalid function argument $ex") # TODO more cases...
    end
    end

    function expand_throws!(ex)
    @assert isexpr(ex, :function)
    @assert isexpr(ex.args[1], :call) # FIXME `where`, etc
    callex = ex.args[1]
    if has_throws(ex)
    # FIXME parameters
    shim = Expr(:function, copy(callex),
    Expr(:call, callex.args[1], Base.identity, map(argname, callex.args[2:end])...))
    insert_wraparg!(ex.args[1], :__exception_matcher__)
    expand_throws_body!(ex.args[2])
    ex = Expr(:toplevel, ex, shim)
    end
    ex
    end

    macro throws(ex)
    esc(expand_throws!(ex))
    end

    #=
    function bar()
    throw ErrorException("Blah")
    @@ -39,22 +132,20 @@ end
    foo("A")
    =#

    # Turns into:
    function bar(_maybe_wrap_error_1)
    e = _maybe_wrap_error_1(ErrorException("Blah"))
    function bar(_match_expected_errors_1)
    e = _match_expected_errors_1(ErrorException("Blah"))
    if e isa ErrorResult
    return e
    else
    throw(e)
    end
    end
    function _foo(_maybe_wrap_error_1, x::Int)
    function foo(_match_expected_errors_1, x::Int)
    if x > 1
    _maybe_wrap_error_2(e) = e isa Exception ? _maybe_wrap_error_1(e) : e
    y = bar(_maybe_wrap_error_2)
    _match_expected_errors_2(e) = e isa Exception ? _match_expected_errors_1(e) : e
    y = bar(_match_expected_errors_2)
    if y isa ErrorResult
    return y
    end
    @@ -64,21 +155,36 @@ function _foo(_maybe_wrap_error_1, x::Int)
    y + 2
    end
    @inline foo(x::Int) = _foo(identity, x)
    @inline foo(x::Int) = foo(identity, x)
    =#

    @throws function bar()
    throw(ErrorException("Blah"))
    end

    @throws function foo(x::Int)
    if x > 1
    y = @throws bar() Exception
    else
    y = 2
    end
    y + 2
    end

    function foo(s::String)
    "Hi "*s
    end
    # Don't really need the following definition, as cannot throw
    # @inline _foo(_maybe_wrap_error_1, s::String) = foo(s)
    # @inline foo(_match_expected_errors_1, s::String) = foo(s)


    x = 2
    # catch
    _maybe_wrap_error_0(e) = e isa ErrorException ? ErrorResult(e) : e
    _match_expected_errors_0(e) = e isa ErrorException ? ErrorResult(e) : e
    # try body
    _maybe_wrap_error_1(e) = e isa Exception ? _maybe_wrap_error_0(e) : e
    z_ = _foo(_maybe_wrap_error_1, x)
    _match_expected_errors_1(e) = e isa Exception ? _match_expected_errors_0(e) : e
    z_ = foo(_match_expected_errors_1, x)
    if !(z_ isa ErrorResult)
    z = z_
    @show z
    @@ -97,4 +203,3 @@ end

    @show foo("A")

    end
  3. c42f created this gist Sep 20, 2019.
    100 changes: 100 additions & 0 deletions exc_lowering.jl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    module Asdf

    struct ErrorResult{E}
    e::E
    end

    #=
    function bar()
    throw ErrorException("Blah")
    end
    function foo(x::Int)
    if x > 1
    y = bar() throws Exception
    else
    y = 2
    end
    y + 2
    end
    function foo(s::String)
    "Hi "*s
    end
    x = 10
    try
    z = foo(x) throws Exception
    @show z
    catch exc::ErrorException
    @show exc
    end
    try
    z = foo(x)
    @show z
    catch exc
    @show exc
    end
    foo("A")
    =#

    # Turns into:
    function bar(_maybe_wrap_error_1)
    e = _maybe_wrap_error_1(ErrorException("Blah"))
    if e isa ErrorResult
    return e
    else
    throw(e)
    end
    end

    function _foo(_maybe_wrap_error_1, x::Int)
    if x > 1
    _maybe_wrap_error_2(e) = e isa Exception ? _maybe_wrap_error_1(e) : e
    y = bar(_maybe_wrap_error_2)
    if y isa ErrorResult
    return y
    end
    else
    y = 2
    end
    y + 2
    end

    @inline foo(x::Int) = _foo(identity, x)

    function foo(s::String)
    "Hi "*s
    end
    # Don't really need the following definition, as cannot throw
    # @inline _foo(_maybe_wrap_error_1, s::String) = foo(s)


    x = 2
    # catch
    _maybe_wrap_error_0(e) = e isa ErrorException ? ErrorResult(e) : e
    # try body
    _maybe_wrap_error_1(e) = e isa Exception ? _maybe_wrap_error_0(e) : e
    z_ = _foo(_maybe_wrap_error_1, x)
    if !(z_ isa ErrorResult)
    z = z_
    @show z
    else
    exc = z_.e
    # catch body
    @show exc
    end

    try
    z = foo(x)
    @show z
    catch exc
    @show exc
    end

    @show foo("A")

    end