API Reference

Syntax Types

Convenient types for storing analysis results of a given Julia Expr, or for creating certain Julia objects easily. These types define some common syntax one would manipulate in Julia meta programming.

Expronicon.JLFunctionType
mutable struct JLFunction <: JLExpr
JLFunction(;kw...)

Type describes a Julia function declaration expression.

Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • head: optional, function head, can be :function, :(=) or :(->).
  • name: optional, function name, can has type Nothing, Symbol or Expr, default is nothing.
  • args: optional, function arguments, a list of Expr or Symbol.
  • kwargs: optional, function keyword arguments, a list of Expr(:kw, name, default).
  • rettype: optional, the explicit return type of a function, can be a Type, Symbol, Expr or just nothing, default is nothing.
  • whereparams: optional, type variables, can be a list of Type, Expr or nothing, default is nothing.
  • body: optional, function body, an Expr, default is Expr(:block).
  • line::LineNumberNode: a LineNumberNode to indicate the line information.
  • doc::String: the docstring of this definition.

Example

Construct a function expression

julia> JLFunction(;name=:foo, args=[:(x::T)], body= quote 1+1 end, head=:function, whereparams=[:T])
function foo(x::T) where {T}
    #= REPL[25]:1 =#    
    1 + 1    
end

Decompose a function expression

julia> ex = :(function foo(x::T) where {T}
           #= REPL[25]:1 =#    
           1 + 1    
       end)
:(function foo(x::T) where T
      #= REPL[26]:1 =#
      #= REPL[26]:3 =#
      1 + 1
  end)

julia> jl = JLFunction(ex)
function foo(x::T) where {T}
    #= REPL[26]:1 =#    
    #= REPL[26]:3 =#    
    1 + 1    
end

Generate Expr from JLFunction

julia> codegen_ast(jl)
:(function foo(x::T) where T
      #= REPL[26]:1 =#
      #= REPL[26]:3 =#
      1 + 1
  end)
source
Expronicon.JLStructType
mutable struct JLStruct <: JLExpr

Type describes a Julia struct.

JLStruct(;kw...)

Create a JLStruct instance.

Available Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • name::Symbol: name of the struct, this is the only required keyword argument.
  • ismutable::Bool: if the struct definition is mutable.
  • typevars::Vector{Any}: type variables of the struct, should be Symbol or Expr.
  • supertype: supertype of the struct definition.
  • fields::Vector{JLField}: field definitions of the struct, should be a JLField.
  • constructors::Vector{JLFunction}: constructors definitions of the struct, should be JLFunction.
  • line::LineNumberNode: a LineNumberNode to indicate the definition position for error report etc.
  • doc::String: documentation string of the struct.
  • misc: other things that happens inside the struct body, by definition this will just fall through and is equivalent to eval them outside the struct body.

Example

Construct a Julia struct.

julia> JLStruct(;name=:Foo, typevars=[:T], fields=[JLField(;name=:x, type=Int)])
struct Foo{T}
    x::Int64
end

Decompose a Julia struct expression

julia> ex = :(struct Foo{T}
           x::Int64
       end)
:(struct Foo{T}
      #= REPL[31]:2 =#
      x::Int64
  end)

julia> jl = JLStruct(ex)
struct Foo{T}
    #= REPL[31]:2 =#
    x::Int64
end

Generate a Julia struct expression

julia> codegen_ast(jl)
:(struct Foo{T}
      #= REPL[31]:2 =#
      x::Int64
  end)
source
Expronicon.JLKwStructType
mutable struct JLKwStruct <: JLExpr
JLKwStruct(;kw...)

Type describes a Julia struct that allows keyword definition of defaults. This syntax is similar to JLStruct except the the fields are of type JLKwField.

Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • name::Symbol: name of the struct, this is the only required keyword argument.
  • typealias::String: an alias of the JLKwStruct, see also the @option macro in Configurations.jl.
  • ismutable::Bool: if the struct definition is mutable.
  • typevars::Vector{Any}: type variables of the struct, should be Symbol or Expr.
  • supertype: supertype of the struct definition.
  • fields::Vector{JLField}: field definitions of the struct, should be a JLField.
  • constructors::Vector{JLFunction}: constructors definitions of the struct, should be JLFunction.
  • line::LineNumberNode: a LineNumberNode to indicate the definition position for error report etc.
  • doc::String: documentation string of the struct.
  • misc: other things that happens inside the struct body, by definition this will just fall through and is equivalent to eval them outside the struct body.
source
Expronicon.JLIfElseType
JLIfElse <: JLExpr
JLIfElse(;kw...)

JLIfElse describes a Julia if ... elseif ... else ... end expression. It allows one to easily construct such expression by inserting condition and code block via a map.

Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • conds::Vector{Any}: expression for the conditions.
  • stmts::Vector{Any}: expression for the statements for corresponding condition.
  • otherwise: the else body.

Example

Construct JLIfElse object

One can construct an ifelse as following

julia> jl = JLIfElse()
nothing

julia> jl[:(foo(x))] = :(x = 1 + 1)
:(x = 1 + 1)

julia> jl[:(goo(x))] = :(y = 1 + 2)
:(y = 1 + 2)

julia> jl.otherwise = :(error("abc"))
:(error("abc"))

julia> jl
if foo(x)
    x = 1 + 1
elseif goo(x)
    y = 1 + 2
else
    error("abc")
end

Generate the Julia Expr object

to generate the corresponding Expr object, one can call codegen_ast.

julia> codegen_ast(jl)
:(if foo(x)
      x = 1 + 1
  elseif goo(x)
      y = 1 + 2
  else
      error("abc")
  end)
source
Expronicon.JLMatchType
JLMatch <: JLExpr

JLMatch describes a Julia pattern match expression defined by MLStyle. It allows one to construct such expression by simply assign each code block to the corresponding pattern expression.

Tip

JLMatch is not available in ExproniconLite since it depends on MLStyle's pattern matching functionality.

Example

One can construct a MLStyle pattern matching expression easily by assigning the corresponding pattern and its result to the map field.

julia> jl = JLMatch(:x)
#= line 0 =#
nothing

julia> jl = JLMatch(:x)
#= line 0 =#
nothing

julia> jl.map[1] = true
true

julia> jl.map[2] = :(sin(x))
:(sin(x))

julia> jl
#= line 0 =#
@match x begin
    1 => true
    2 => sin(x)
    _ =>     nothing
end

to generate the corresponding Julia Expr object, one can call codegen_ast.

julia> codegen_ast(jl)
:(let
      true
      var"##return#263" = nothing
      var"##265" = x
      if var"##265" isa Int64
          #= line 0 =#
          if var"##265" === 1
              var"##return#263" = let
                      true
                  end
              #= unused:1 =# @goto var"####final#264#266"
          end
          #= line 0 =#
          if var"##265" === 2
              var"##return#263" = let
                      sin(x)
                  end
              #= unused:1 =# @goto var"####final#264#266"
          end
      end
      #= line 0 =#
      begin
          var"##return#263" = let
                  nothing
              end
          #= unused:1 =# @goto var"####final#264#266"
      end
      (error)("matching non-exhaustive, at #= line 0 =#")
      #= unused:1 =# @label var"####final#264#266"
      var"##return#263"
  end)
source
Expronicon.JLFieldType
mutable struct JLField <: JLExpr
JLField(;kw...)

Type describes a Julia field in a Julia struct.

Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • name::Symbol: the name of the field.
  • type: the type of the field.
  • line::LineNumberNode: a LineNumberNode to indicate the line information.
  • doc::String: the docstring of this definition.
source
Expronicon.JLKwFieldType
mutable struct JLKwField <: JLExpr

Type describes a Julia field that can have a default value in a Julia struct.

JLKwField(;kw...)

Create a JLKwField instance.

Fields and Keyword Arguments

All the following fields are valid as keyword arguments kw in the constructor, and can be access via <object>.<field>.

The only required keyword argument for the constructor is name, the rest are all optional.

  • name::Symbol: the name of the field.
  • type: the type of the field.
  • default: default value of the field, default is no_default.
  • line::LineNumberNode: a LineNumberNode to indicate the line information.
  • doc::String: the docstring of this definition.
source

Analysis

Functions for analysing a given Julia Expr, e.g splitting Julia function/struct definitions etc.

Expronicon.JLForMethod
JLFor(ex::Expr)

Create a JLFor from given Julia for loop expression.

Example

julia> ex = @expr for i in 1:10, j in 1:j
           M[i, j] += 1
       end
:(for i = 1:10, j = 1:j
      #= REPL[3]:2 =#
      M[i, j] += 1
  end)

julia> jl = JLFor(ex)
for i in 1 : 10,
    j in 1 : j
    #= loop body =#
    begin
        #= REPL[3]:2 =#        
        M[i, j] += 1        
    end
end

julia> jl.vars
2-element Vector{Any}:
 :i
 :j

julia> jl.iterators
2-element Vector{Any}:
 :(1:10)
 :(1:j)
source
Expronicon.JLFunctionMethod
JLFunction(ex::Expr)

Create a JLFunction object from a Julia function Expr.

Example

julia> JLFunction(:(f(x) = 2))
f(x) = begin
    #= REPL[37]:1 =#    
    2    
end
source
Expronicon.JLIfElseMethod
JLIfElse(ex::Expr)

Create a JLIfElse from given Julia ifelse Expr.

Example

julia> ex = :(if foo(x)
             x = 1 + 1
         elseif goo(x)
             y = 1 + 2
         else
             error("abc")
         end)
:(if foo(x)
      #= REPL[41]:2 =#
      x = 1 + 1
  elseif #= REPL[41]:3 =# goo(x)
      #= REPL[41]:4 =#
      y = 1 + 2
  else
      #= REPL[41]:6 =#
      error("abc")
  end)

julia> JLIfElse(ex)
if foo(x)
    begin
        #= REPL[41]:2 =#        
        x = 1 + 1        
    end
elseif begin
    #= REPL[41]:3 =#    
    goo(x)    
end
    begin
        #= REPL[41]:4 =#        
        y = 1 + 2        
    end
else
    begin
        #= REPL[41]:6 =#        
        error("abc")        
    end
end
source
Expronicon.JLKwStructType
JLKwStruct(ex::Expr, typealias=nothing)

Create a JLKwStruct from given Julia struct Expr, with an option to attach an alias to this type name.

Example

julia> JLKwStruct(:(struct Foo
           x::Int = 1
       end))
#= kw =# struct Foo
    #= REPL[39]:2 =#
    x::Int = 1
end
source
Expronicon.JLStructMethod
JLStruct(ex::Expr)

Create a JLStruct object from a Julia struct Expr.

Example

julia> JLStruct(:(struct Foo
           x::Int
       end))
struct Foo
    #= REPL[38]:2 =#
    x::Int
end
source
Expronicon.compare_exprMethod
compare_expr(lhs, rhs)

Compare two expression of type Expr or Symbol semantically, which:

  1. ignore the detail value LineNumberNode in comparision
  2. ignore the detailed name of typevars in Expr(:curly, ...) or Expr(:where, ...)

This gives a way to compare two Julia expression semantically which means although some details of the expression is different but they should produce the same lowered code.

source
Expronicon.compare_varsMethod
compare_vars(lhs, rhs)

Compare two expression by assuming all Symbols are variables, thus their value doesn't matter, only where they are matters under this assumption. See also compare_expr.

source
Expronicon.has_kwfn_constructorFunction
has_kwfn_constructor(def[, name = struct_name_plain(def)])

Check if the struct definition contains keyword function constructor of name. The constructor name to check by default is the plain constructor which does not infer any type variables and requires user to input all type variables. See also struct_name_plain.

source
Expronicon.has_plain_constructorFunction
has_plain_constructor(def, name = struct_name_plain(def))

Check if the struct definition contains the plain constructor of name. By default the name is the inferable name struct_name_plain.

Example

def = @expr JLKwStruct struct Foo{T, N}
    x::Int
    y::N

    Foo{T, N}(x, y) where {T, N} = new{T, N}(x, y)
end

has_plain_constructor(def) # true

def = @expr JLKwStruct struct Foo{T, N}
    x::T
    y::N

    Foo(x, y) = new{typeof(x), typeof(y)}(x, y)
end

has_plain_constructor(def) # false

the arguments must have no type annotations.

def = @expr JLKwStruct struct Foo{T, N}
    x::T
    y::N

    Foo{T, N}(x::T, y::N) where {T, N} = new{T, N}(x, y)
end

has_plain_constructor(def) # false
source
Expronicon.split_function_headMethod
split_function_head(ex::Expr) -> name, args, kw, whereparams, rettype

Split function head to name, arguments, keyword arguments and where parameters.

source
Expronicon.split_structMethod
split_struct(ex::Expr) -> ismutable, name, typevars, supertype, body

Split struct definition head and body.

source
Expronicon.split_struct_nameMethod
split_struct_name(ex::Expr) -> name, typevars, supertype

Split the name, type parameters and supertype definition from struct declaration head.

source
Expronicon.@exprMacro
@expr <type> <expression>

Return the expression in given type.

Example

julia> ex = @expr JLKwStruct struct Foo{N, T}
           x::T = 1
       end
#= kw =# struct Foo{N, T}
    #= /home/roger/code/julia/Expronicon/test/analysis.jl:5 =#
    x::T = 1
end
source
Expronicon.@exprMacro
@expr <expression>

Return the original expression object.

Example

julia> ex = @expr x + 1
:(x + 1)
source
Expronicon.@test_exprMacro
@test_expr <type> <ex>

Test if the syntax type generates the same expression ex. Returns the corresponding syntax type instance. Requires using Test before using this macro.

Example

def = @test_expr JLFunction function (x, y)
    return 2
end
@test is_kw_fn(def) == false
source
Expronicon.@test_exprMacro
@test_expr <expr> == <expr>

Test if two expression is equivalent semantically, this uses compare_expr to decide if they are equivalent, ignores things such as LineNumberNode generated Symbol in Expr(:curly, ...) or Expr(:where, ...).

source

Transform

Some common transformations for Julia Expr, these functions takes an Expr and returns an Expr.

Expronicon.name_onlyMethod
name_only(ex)

Remove everything else leaving just names, currently supports function calls, type with type variables, subtype operator <: and type annotation ::.

Example

julia> using Expronicon

julia> name_only(:(sin(2)))
:sin

julia> name_only(:(Foo{Int}))
:Foo

julia> name_only(:(Foo{Int} <: Real))
:Foo

julia> name_only(:(x::Int))
:x
source
Expronicon.prettifyMethod
prettify(ex; kw...)

Prettify given expression, remove all LineNumberNode and extra code blocks.

Options (Kwargs)

All the options are true by default.

  • rm_lineinfo: remove LineNumberNode.
  • flatten_blocks: flatten begin ... end code blocks.
  • rm_nothing: remove nothing in the begin ... end.
  • rm_single_block: remove single begin ... end.
  • alias_gensym: replace ##<name>#<num> with <name>_<id>.
Tips

the LineNumberNode inside macro calls won't be removed since the macrocall expression requires a LineNumberNode. See also issues/#9.

source
Expronicon.rm_lineinfoMethod
rm_lineinfo(ex)

Remove LineNumberNode in a given expression.

Tips

the LineNumberNode inside macro calls won't be removed since the macrocall expression requires a LineNumberNode. See also issues/#9.

source

CodeGen

Code generators, functions that generates Julia Expr from given arguments, Expronicon types.

Expronicon.codegen_astMethod
codegen_ast(def)

Generate Julia AST object Expr from a given syntax type.

Example

One can generate the Julia AST object from a JLKwStruct syntax type.

julia> def = @expr JLKwStruct struct Foo{N, T}
                  x::T = 1
              end
#= kw =# struct Foo{N, T}
    #= REPL[19]:2 =#
    x::T = 1
end

julia> codegen_ast(def)|>rm_lineinfo
quote
    struct Foo{N, T}
        x::T
    end
    begin
        function Foo{N, T}(; x = 1) where {N, T}
            Foo{N, T}(x)
        end
        function Foo{N}(; x::T = 1) where {N, T}
            Foo{N, T}(x)
        end
    end
end
source
Expronicon.codegen_ast_fieldsMethod
codegen_ast_fields(fields; just_name::Bool=true)

Generate a list of Julia AST object for each field, only generate a list of field names by default, option just_name can be turned off to call codegen_ast on each field object.

source
Expronicon.codegen_ast_kwfnFunction
codegen_ast_kwfn(def[, name = nothing])

Generate the keyword function from a Julia struct definition.

Example

julia> def = @expr JLKwStruct struct Foo{N, T}
                  x::T = 1
              end
#= kw =# struct Foo{N, T}
    #= REPL[19]:2 =#
    x::T = 1
end

julia> codegen_ast_kwfn(def)|>prettify
quote
    function Foo{N, T}(; x = 1) where {N, T}
        Foo{N, T}(x)
    end
    function Foo{N}(; x::T = 1) where {N, T}
        Foo{N, T}(x)
    end
end

julia> def = @expr JLKwStruct struct Foo
                  x::Int = 1
              end
#= kw =# struct Foo
    #= REPL[23]:2 =#
    x::Int = 1
end

julia> codegen_ast_kwfn(def)|>prettify
quote
    function Foo(; x = 1)
        Foo(x)
    end
    nothing
end
source
Expronicon.codegen_ast_kwfn_plainFunction
codegen_ast_kwfn_plain(def[, name = nothing])

Generate the plain keyword function that does not infer type variables. So that one can use the type conversions defined by constructors.

source
Expronicon.codegen_ast_structMethod
codegen_ast_struct(def)

Generate pure Julia struct Expr from struct definition. This is equivalent to codegen_ast for JLStruct. See also codegen_ast.

Example

julia> def = JLKwStruct(:(struct Foo
           x::Int=1
           
           Foo(x::Int) = new(x)
       end))
struct Foo
    x::Int = 1
end

julia> codegen_ast_struct(def)
:(struct Foo
      #= REPL[21]:2 =#
      x::Int
      Foo(x::Int) = begin
              #= REPL[21]:4 =#
              new(x)
          end
  end)
source
Expronicon.codegen_ast_struct_bodyMethod
codegen_ast_struct_body(def)

Generate the struct body.

Example

julia> def = JLStruct(:(struct Foo
           x::Int
           
           Foo(x::Int) = new(x)
       end))
struct Foo
    x::Int
end

julia> codegen_ast_struct_body(def)
quote
    #= REPL[15]:2 =#
    x::Int
    Foo(x::Int) = begin
            #= REPL[15]:4 =#
            new(x)
        end
end
source
Expronicon.codegen_ast_struct_headMethod
codegen_ast_struct_head(def)

Generate the struct head.

Example

julia> using Expronicon

julia> def = JLStruct(:(struct Foo{T} end))
struct Foo{T}
end

julia> codegen_ast_struct_head(def)
:(Foo{T})

julia> def = JLStruct(:(struct Foo{T} <: AbstractArray end))
struct Foo{T} <: AbstractArray
end

julia> codegen_ast_struct_head(def)
:(Foo{T} <: AbstractArray)
source
Expronicon.struct_name_without_inferableMethod
struct_name_without_inferable(def; leading_inferable::Bool=true)

Constructor name that assume some of the type variables is inferred. See also struct_name_plain. The kwarg leading_inferable can be used to configure whether to preserve the leading inferable type variables, the default is true to be consistent with the default julia constructors.

Example

julia> def = @expr JLKwStruct struct Foo{N, Inferable}
    x::Inferable = 1
end

julia> struct_name_without_inferable(def)
:(Foo{N})

julia> def = @expr JLKwStruct struct Foo{Inferable, NotInferable}
    x::Inferable
end

julia> struct_name_without_inferable(def; leading_inferable=true)
:(Foo{Inferable, NotInferable})

julia> struct_name_without_inferable(def; leading_inferable=false)
:(Foo{NotInferable})
source
Expronicon.xcallMethod
xcall(m::Module, name::Symbol, args...; kw...)

Create a function call to GlobalRef(m, name).

Tip

due to Revise/#616, to make your macro work with Revise, we use the dot expression Expr(:., <module>, QuoteNode(<name>)) instead of GlobalRef here.

source

Printings

Pretty printing functions.