``````

In [1]:

# For shits and giggles
using Distributions
import Distributions.rand

# Let's say all variables will have a state and domain

type Domain
from::Int
to::Int
end

#Test
function rand(d::Domain)
du = Distributions.Uniform(d.from, d.to)
return rand(du)
end

``````
``````

Out[1]:

rand (generic function with 111 methods)

``````
``````

In [5]:

``````
``````

In [7]:

``````
``````

In [607]:

``````
``````

In [1]:

function parse_from_to(expr)
println(expr)
tp = nothing
from = expr.args[2]
to = expr.args[3]
if (typeof(from) != Symbol)
tp = typeof(from)
elseif (typeof(from) != Symbol)
tp = typeof(to)
end

return from, to, tp
end

function parse_dims(expr)
dim1 = 1
dim2 = 1
if ((typeof(expr) != Expr) && (typeof(expr) == Symbol || isinteger(expr)))
dim1 = expr
elseif (length(expr.args) == 2)
dim1 = expr.args[1]
dim2 = expr.args[2]
end

return (dim1, dim2)
end

function pb(expr::Expr, idx=-1)
s = string(expr)
if (contains(s, ">>>=") || contains(s, ".//="))
error("Sorry, you caught the dirty hack, can't parse that.")
end
expr = parse(replace(s, "::", ">>>=")) #Manipulate the precedence priority

# If it is a block, replace it by the block's contents
expr = expr.args[length(expr.args)]
end

# Left is now new
l = expr.args[1]
if (typeof(l) == Symbol) #Great, no array
var = l
else #array
var = l.args[1]
if idx == -1
idx = l.args[2]
end
end

# Right is now the type
# How do I macros in macros?
expr = parse(replace(string(expr.args[2]), "^", ".//="))
from = :inf
to = :inf
tp = nothing
dims = nothing

if (typeof(expr) == Symbol)
tp = expr
println(expr.args)
# Left we either have the type# or from_to
if (typeof(expr.args[1]) == Symbol)
tp = expr.args[1]
else
from, to, tp = parse_from_to(expr.args[1])
end
# Right we have either one dimension, or 2
dims = parse_dims(expr.args[2])
elseif (expr.args[1] == :..)
from, to, tp = parse_from_to(expr)

else
#println(expr.args)
end

# Clean up
if dims == nothing || dims == 1
dims = (1,1)
end
#TODO: Think about concrete types. Inference will be hard for large domains.
typelookup = Dict()
typelookup[:float64] = Float64
typelookup[:Float64] = Float64
typelookup[:int] = Int
typelookup[:Integer] = Int64
typelookup[:integer] = Int64

tp = typelookup[tp]
end
return var, idx, from, to, tp, dims
end

``````
``````

Out[1]:

pb (generic function with 2 methods)

``````
``````

In [19]:

function parse_pt(arg::Expr, consts, hyperparams, params, idx=-1, distvalue=None)

before = (length(consts), length(hyperparams), length(params))

if(arg.args[1] in Set{Symbol}([symbol("@constant"),  symbol("@hyperparam"), symbol("@param")]))
(var, idx, from, to, tp, dims) = pb(arg.args[2], idx)

if(arg.args[1] == symbol("@constant"))
consts[var] = Set()
end
push!(consts[var], (var, idx, from, to, tp, dims, :const))

elseif(arg.args[1] == symbol("@hyperparam"))
hyperparams[var] = Set()
end
push!(hyperparams[var], (var, idx, from, to, tp, dims, :hyperparam))
elseif(arg.args[1] == symbol("@param"))
params[var] = Set()
end
push!(params[var], (var, idx, from, to, tp, dims, :unk))
else
println("This should not happen")
end

if arg.args[1] == symbol("@~") # type foo ~ bar
println("ARE WE HAVING FUN YET?")
println(arg.args[2], " -- of type -- ",  typeof(arg.args[2]), " ", arg.args[2].head)
(var, idx, from, to, tp, dims) = pb(arg.args[2], idx)
println(arg.args[3], " -- of type -- ",  typeof(arg.args[3]), " ",)
#(var, idx, from, to, tp, dims, _) = parse_pt(Expr(:macrocall, :@param, arg.args[2]), consts, hyperparams, params, idx, arg.args[3])
params[var] = Set()
end
push!(params[var], (var, idx, from, to, tp, dims, arg.args[3]))
end

else
println("parse_pt called with something not a line: ", arg.head)
println(arg.args)
end
end

after = (length(consts), length(hyperparams), length(params))
if before != after && false
println("Changed params")
end
return consts, hyperparams, params
end
macro model(name, rest...)

eval(:(model = \$(string(name)))) #Set the current model in the global scope
println(model)

#TODO: By using arrays instead, and indexing by them you can save much space
# No need for globalization! :-)

#eval(:(consts = Dict())) # All of these should be specified
#eval(:(hyperparams = Dict())) # When initializing for now
#eval(:(params = Dict()))

consts = Dict()
hyperparams = Dict()
params = Dict()

c = 0
u = 0

# First pass to establish parameters
for(i in rest) # For every top line
if (typeof(i) == Expr) # If it is not a comment
for (arg in i.args)
if(typeof(arg) == Expr)
consts, hyperparams, params = parse_pt(arg, consts, hyperparams, params)
end
end
end
end

# This closure expects them to create the function
fparamnames = {}
for k in keys(consts)
push!(fparamnames,k )
end
for k in keys(hyperparams)
push!(fparamnames, k)
end

println(fparamnames)

f = parse("function " * model * " (;ks... ) end")
f.args[2] =

quote

paramdict = Dict{}()
for (k,v) in ks
paramdict[k]=v
end

# Arguments: the model parameters. Returns success or false or something
#Initialize the arguments

#Stupid 2 pass, now parameters are established

# Replace constants and metaprameters
for cs in \$consts #cs = (:name, {})
if cs[1] in keys(paramdict)
s = deepcopy(cs[2]) #Copy set to modify the original
v = paramdict[cs[1]]
for c in s
# See if the current value is :constant instead of its value
if c[7] == :const
c_updated = (c[1], c[2], c[3], c[4], c[5], c[6], v) #TODO
delete!(cs[2], c)
push!(cs[2], c_updated)
end
end
end
end
#Do the same for metaparameters (TODO: DRY) (TODO: merge sets or something with a merge(set1,set2) that works)
for hp in \$hyperparams # = (:name, {})
if hp[1] in keys(paramdict)
s = deepcopy(hp[2]) #Copy set to modify the original
v = paramdict[hp[1]]
for c in s
# See if the current value is :hyperparameter instead of its value
if c[7] == :hyperparam
c_updated = (c[1], c[2], c[3], c[4], c[5], c[6], v) #TODO
delete!(hp[2], c)
push!(hp[2], c_updated)
end
end
end
end

for(i in \$rest) # For every top line
if (typeof(i) == Expr) # If it is not a comment
for (arg in i.args)
#println(arg)
if(typeof(arg) == Expr)
if(arg.head == :for) # We have a loop
boundary = arg.args[1]
loopvar = boundary.args[1]
lfrom = boundary.args[2].args[1]
lto = boundary.args[2].args[2]
contents = arg.args[2]
#println(boundary)
println(loopvar," ", typeof(loopvar)," lfrom: ", lfrom, " ", typeof(lfrom), " lto: ", Dict(ks)[symbol(lto)], " ", typeof(lto))
#println(contents)

# For this part you need the defined fparamnames, which are given to the constructor

for k in lfrom:Dict(ks)[symbol(lto)]
#println(contents)
for l in contents.args
if (typeof(l) == Expr) # If it is not a comment
println("------start------")
#println("**")
#println(l.args)
consts, hyperparams, params = parse_pt(l, \$consts, \$hyperparams, \$params, k)
#TODO: If type == bla ~ blu then ...
#end
println("------end-------")
end
#    consts, hyperparams, params = parse_pt(arg, consts, hyperparams, params, idx=k)
end
end
else
#println("\"", arg, "\" which is of type:", typeof(arg)," has args of type: ", typeof(arg.args[1]))
end
end
end
end
end

return \$consts, \$hyperparams, \$params

end # End inner constructor function

return f

end # End macro

gmm = @model GaussianMixtureModel begin
# constant declaration
@constant d::Int   # vector dimension
@constant n::Int   # number of observations
@hyperparam K::Int   # number of components

# parameter declaration
@param pi :: (0.0..1.0)^K    # prior proportions
for k in 1 : K
@param mu[k] :: Float64^d         # component mean
@param sig[k] :: Float64^(d, d)   # component covariance
end

# sample generation process
for i in 1 : n
z[i] ~ Categorical(pi)
x[i] ~ MultivariateNormal(mu[z[i]], sig[z[i]])
end
end

consts, hyperparams, params = gmm(d=2,n=2,K=5)
println(consts)
print(hyperparams)

``````
``````

GaussianMixtureModel
{:(0.0 .. 1.0),:K}
0.0 .. 1.0
parse_pt called with something not a line: for
{:(k = 1:K),quote  # line 206:
@param mu[k]::Float64^d # line 207:
@param sig[k]::Float64^(d,d)
end}
parse_pt called with something not a line: for
{:(i = 1:n),quote  # line 212:
@~ z[i] Categorical(pi) # line 213:
@~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]])
end}
{:n,:d,:K}
Replacing consts.
*****************
Replaced consts.
*****************
k Symbol lfrom: 1 Int64 lto: 5 Symbol
------start------
# line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
# line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
# line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
# line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
# line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
# line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
# line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
# line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
------start------
# line 206: Symbol
------end-------
------start------
@param mu[k]::Float64^d Symbol
{:Float64,:d}
------end-------
------start------
# line 207: Symbol
------end-------
------start------
@param sig[k]::Float64^(d,d) Symbol
{:Float64,:((d,d))}
------end-------
i Symbol lfrom: 1 Int64 lto: 2 Symbol
------start------
# line 212: Symbol
------end-------
------start------
@~ z[i] Categorical(pi) Symbol
ARE WE HAVING FUN YET?
z[i] -- of type -- Expr ref
Categorical(pi) -- of type -- Expr
------end-------
------start------
# line 213: Symbol
------end-------
------start------
@~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]]) Symbol
ARE WE HAVING FUN YET?
x[i] -- of type -- Expr ref
MultivariateNormal(mu[z[i]],sig[z[i]]) -- of type -- Expr
------end-------
------start------
# line 212: Symbol
------end-------
------start------
@~ z[i] Categorical(pi) Symbol
ARE WE HAVING FUN YET?
z[i] -- of type -- Expr ref
Categorical(pi) -- of type -- Expr
------end-------
------start------
# line 213: Symbol
------end-------
------start------
@~ x[i] MultivariateNormal(mu[z[i]],sig[z[i]]) Symbol
ARE WE HAVING FUN YET?
x[i] -- of type -- Expr ref
MultivariateNormal(mu[z[i]],sig[z[i]]) -- of type -- Expr
------end-------
{:n=>Set{Any}({(:n,-1,:inf,:inf,:Int,(1,1),2)}),:d=>Set{Any}({(:d,-1,:inf,:inf,:Int,(1,1),2)})}
{:K=>Set{Any}({(:K,-1,:inf,:inf,:Int,(1,1),5)})}

``````
``````

In [44]:

params

``````
``````

Out[44]:

Dict{Any,Any} with 3 entries:
:pi  => Set{Any}({(:pi,-1,0.0,1.0,Float64,(:K,1))})
:sig => Set{Any}({(:sig,3,:inf,:inf,:Float64,(:d,:d)),(:sig,1,:inf,:inf,:Floa…
:mu  => Set{Any}({(:mu,1,:inf,:inf,:Float64,(:d,1)),(:mu,3,:inf,:inf,:Float64…

``````
``````

In [33]:

macroexpand(quote f_test =
@model GaussianMixtureModel begin
# constant declaration
@constant d::Int   # vector dimension
@constant n::Int   # number of observations
@hyperparam K::Int   # number of components

# parameter declaration
@param pi :: (0.0..1.0)^K    # prior proportions
for k in 1 : K
@param mu[k] :: Float64^d         # component mean
@param sig[k] :: Float64^(d, d)   # component covariance
end

# sample generation process
for i in 1 : n
z[i] ~ Categorical(pi)
x[i] ~ MultivariateNormal(mu[z[i]], sig[z[i]])
end
end end)
1

``````
``````

GaussianMixtureModel
{:(0.0 .. 1.0),:K}
0.0 .. 1.0
{:n,:d,:K}

Out[33]:

1

``````
``````

In [8]:

f2 = eval(parse("function f2(x=1) println(x) end"))

``````
``````

Out[8]:

f2 (generic function with 2 methods)

``````
``````

In [12]:

GaussianMixtureModel()

``````
``````

GaussianMixtureModel not defined

``````
``````

In [27]:

consts, hyperparams, params = f_test(d=2,n=2,K=5)

``````
``````

k Symbol lfrom: 1 Int64 lto: 5 Symbol
Expr # line 11:
Expr@param mu[k]::Float64^d

consts not defined

in #1277#GaussianMixtureModel at In[25]:74

``````
``````

In [97]:

Dict(dic)[:K]

``````
``````

Out[97]:

5

``````
``````

In [24]:

params

``````
``````

Out[24]:

Dict{Any,Any} with 1 entry:
:pi => Set{Any}({(:pi,-1,0.0,1.0,Float64,(:K,1))})

``````
``````

In [25]:

hyperparams

``````
``````

Out[25]:

Dict{Any,Any} with 1 entry:
:K => Set{Any}({(:K,-1,:inf,:inf,:Int,nothing)})

``````
``````

In [26]:

consts

``````
``````

Out[26]:

Dict{Any,Any} with 2 entries:
:n => Set{Any}({(:n,-1,:inf,:inf,:Int,nothing)})
:d => Set{Any}({(:d,-1,:inf,:inf,:Int,nothing)})

``````
``````

In [7]:

println(typeof(:(function uu(x=1, y=2) println(x) end).args[1]))

``````
``````

Expr

``````
``````

In [392]:

function uuu(a;args...)
println(args)
end

``````
``````

Out[392]:

uuu (generic function with 3 methods)

``````
``````

In [395]:

macro mkfun(args)
quote
local t0 = time()
local val = \$(esc(ex))
local t1 = time()
println("elapsed time: ", t1-t0, " seconds")
val
end
end

``````
``````

unsupported or misplaced expression =>

``````
``````

In [39]:

macroexpand(quote f_test(d=2,n=2,K=5) end)

``````
``````

Out[39]:

quote  # In[39], line 1:
f_test(d=2,n=2,K=5)
end

``````
``````

In [21]:

function parse_name(s)
in_under = false
symbols = String[""]
for c in s
if in_under
if c == '_'
symbols[end] *= "_"
else
push!(symbols,string(c))
end
in_under = false
else
if c == '_'
in_under = true
else
symbols[end] *= string(c)
in_under = false
end
end
end
symbols
end

function foo(a)
return a
end

``````
``````

Out[21]:

foo (generic function with 1 method)

``````
``````

In [24]:

macro funmac(e)
name = string(e)
println("Name: ", name)
syms = parse_name(name)
syms = map(symbol, syms)
print(syms)
quote
print(\$(syms[2]))
\$(syms[1])(\$(syms[2:end]...))
end
end

``````
``````

In [28]:

macroexpand( quote @funmac(foo_11) end )

``````
``````

Name: foo_11
[:foo,symbol("11")]

Out[28]:

quote  # In[28], line 1:
begin  # In[24], line 8:
print(11) # line 9:
foo(11)
end
end

``````
``````

In [30]:

``````
``````

2

``````