<commit_before>
import Base: >>>

export stoppropagation,
       addinterpreter,
       constant,
       pairwith,
       sampler,
       sample,
       trigger!,
       watch!,
       pairwithindex,
       subscribe
#=
   Note: Maybe use

   immutable MessageException <: Exception
       error::Exception
       backtrace::String
   end

   MessageException(x::Exception) =
       MessageException(x, sprint(io -> Base.show_backtrace(io, backtrace())))

=#

@doc """
A Behavior is a tile that denotes that it can
broadcast some signal
""" ->
abstract Behavior <: Tile

name(b::Behavior) = b.name

broadcast(b::Behavior) = b

## Interpreting a message ##

abstract Interpreter

immutable WithInterpreter <: Behavior
    interpreter::Interpreter
    tile::Behavior
end

# Identity interpreter: don't change the message
immutable Id <: Interpreter end
const identity = Id()

# Chain two interpreters together.
immutable Chained <: Interpreter
    interpreter1::Interpreter
    interpreter2::Interpreter
end

Chained(::Id, ::Id) = identity
Chained(a::Interpreter, ::Id) = a
Chained(::Id, a::Interpreter) = a

addinterpreter(i::Interpreter, tile::Behavior) =
    # Chained is defined below
    WithInterpreter(Chained(i, default_interpreter(tile)), tile)
addinterpreter(i::Interpreter) = t -> addinterpreter(i, t)

default_interpreter(t::WithInterpreter) = t.interpreter


# The default interpreter for a behavior is identity
default_interpreter(::Behavior) = identity

interpret(dec::Id, x) = x

# convert to a type
immutable ToType{T} <: Interpreter end
interpret{T}(::ToType{T}, x) = convert(T, x)
interpret{T<:Integer}(::ToType{T}, x::String) =
    try parse(T, x) catch ex ex end
interpret{T<:Real}(::ToType{T}, x::Nothing) = zero(T)
interpret(::ToType{@compat AbstractString}, x::Nothing) = ""

interpret(dec::Chained, x) =
    interpret(dec.interpreter1, interpret(dec.interpreter2, x))

# Pair with a constant
immutable PairWith <: Interpreter
    value::Any
end

interpret(dec::PairWith, x) = (x, dec.value)

pairwith(x) = addinterpreter(PairWith(x))
pairwith(x, tile::Tile) = addinterpreter(PairWith(x), tile)
pairwith(x::AbstractArray, tiles::AbstractArray) = map(pairwith, x, tiles)
pairwith(x, tiles::AbstractArray) = map(pairwith(x))

pairwithindex(tiles::AbstractVector) =
    [pairwith(i, v)
        for (i, v) in enumerate(tiles)]

pairwithindex(tiles::AbstractMatrix) =
    [pairwith((i, j), tiles[i, j])
        for i=1:size(tiles, 1), j=1:size(tiles, 2)]

# Instead of the signal value, use a constant
immutable Const <: Interpreter
    value::Any
end

interpret(dec::Const, _) = dec.value

constant(x, tile) = addinterpreter(Const(x), tile)
constant(x) = addinterpreter(Const(x))
constant(xs::AbstractArray, tiles::AbstractArray) = map(constant, xs, tiles)
constant(x, tiles::AbstractArray) = map(constant(x), tiles)

# Apply a function
immutable InterpreterFn <: Interpreter
    f::Function
end
addinterpreter(f::Function, tile::Tile) = addinterpreter(InterpreterFn(f), tile)
interpret(dec::InterpreterFn, x) = dec.f(x)

# Apply a function with some constant partial args
# For efficiency, the function must be defined outside
# of any signal functions. i.e. no point creating
# ad-hoc functions

immutable InterpreterThunk <: Interpreter
    f::Function
    args::Tuple
end
partial(f::Function, args::Tuple, tile) =
    addinterpreter(InterpreterThunk(f, args), tile)

interpret(dec::InterpreterThunk, x) = dec.f(dec.args..., x)


name(d::WithInterpreter) = name(d.tile)

render(d::WithInterpreter, state) = render(d.tile, state)

#
# Subscribe to a signal and register an interpreter
#

immutable Subscription <: Tile
    tile::Tile
    name::Symbol # Name of the signal update on the front-end
    receiver::@compat Tuple{Interpreter, Input}
end

subscribe(t::Tile, name, s; absorb=true) =
    Subscription(t, name, s) |>
       (x -> absorb ? stoppropagation(x, name) : x)

subscribe(t::Behavior, s::Input; absorb=true) =
    subscribe(t, name(t), (default_interpreter(t), s), absorb=absorb)

subscribe(t::Behavior, s::(@compat Tuple{Interpreter, Input}); absorb=true) =
    subscribe(t, name(t), s, absorb=absorb)

subscribe(t::WithInterpreter, s::Input; absorb=true) =
    subscribe(t.tile, name(t), (t.interpreter, s), absorb=absorb)

render(sig::Subscription, state) =
    render(sig.tile, state) <<
        Elem("signal-transport",
            # Note: setup_transport here adds (interpreter, input) pair
            # to a dict, returns the key - this fn is idempotent
            name=sig.name, signalId=makeid(sig.receiver))

(>>>)(b::Behavior, s::Input) = subscribe(b, s)

### Sampling

@api sampler => Sampler <: Interpreter begin
    arg(triggers::Dict)
    arg(watched::Dict)
end
sampler() = Sampler(Dict(), Dict())

interpret(s::Sampler, msg) = begin
    try
        d = Dict()
        d[:_trigger] = symbol(msg["_trigger"])
<commit_msg>Add back signal_transport for IJulia<commit_after>
import Base: >>>

export stoppropagation,
       addinterpreter,
       constant,
       pairwith,
       sampler,
       sample,
       trigger!,
       watch!,
       pairwithindex,
       subscribe
#=
   Note: Maybe use

   immutable MessageException <: Exception
       error::Exception
       backtrace::String
   end

   MessageException(x::Exception) =
       MessageException(x, sprint(io -> Base.show_backtrace(io, backtrace())))

=#

@doc """
A Behavior is a tile that denotes that it can
broadcast some signal
""" ->
abstract Behavior <: Tile

name(b::Behavior) = b.name

broadcast(b::Behavior) = b

## Interpreting a message ##

abstract Interpreter

immutable WithInterpreter <: Behavior
    interpreter::Interpreter
    tile::Behavior
end

# Identity interpreter: don't change the message
immutable Id <: Interpreter end
const identity = Id()

# Chain two interpreters together.
immutable Chained <: Interpreter
    interpreter1::Interpreter
    interpreter2::Interpreter
end

Chained(::Id, ::Id) = identity
Chained(a::Interpreter, ::Id) = a
Chained(::Id, a::Interpreter) = a

addinterpreter(i::Interpreter, tile::Behavior) =
    # Chained is defined below
    WithInterpreter(Chained(i, default_interpreter(tile)), tile)
addinterpreter(i::Interpreter) = t -> addinterpreter(i, t)

default_interpreter(t::WithInterpreter) = t.interpreter


# The default interpreter for a behavior is identity
default_interpreter(::Behavior) = identity

interpret(dec::Id, x) = x

# convert to a type
immutable ToType{T} <: Interpreter end
interpret{T}(::ToType{T}, x) = convert(T, x)
interpret{T<:Integer}(::ToType{T}, x::String) =
    try parse(T, x) catch ex ex end
interpret{T<:Real}(::ToType{T}, x::Nothing) = zero(T)
interpret(::ToType{@compat AbstractString}, x::Nothing) = ""

interpret(dec::Chained, x) =
    interpret(dec.interpreter1, interpret(dec.interpreter2, x))

# Pair with a constant
immutable PairWith <: Interpreter
    value::Any
end

interpret(dec::PairWith, x) = (x, dec.value)

pairwith(x) = addinterpreter(PairWith(x))
pairwith(x, tile::Tile) = addinterpreter(PairWith(x), tile)
pairwith(x::AbstractArray, tiles::AbstractArray) = map(pairwith, x, tiles)
pairwith(x, tiles::AbstractArray) = map(pairwith(x))

pairwithindex(tiles::AbstractVector) =
    [pairwith(i, v)
        for (i, v) in enumerate(tiles)]

pairwithindex(tiles::AbstractMatrix) =
    [pairwith((i, j), tiles[i, j])
        for i=1:size(tiles, 1), j=1:size(tiles, 2)]

# Instead of the signal value, use a constant
immutable Const <: Interpreter
    value::Any
end

interpret(dec::Const, _) = dec.value

constant(x, tile) = addinterpreter(Const(x), tile)
constant(x) = addinterpreter(Const(x))
constant(xs::AbstractArray, tiles::AbstractArray) = map(constant, xs, tiles)
constant(x, tiles::AbstractArray) = map(constant(x), tiles)

# Apply a function
immutable InterpreterFn <: Interpreter
    f::Function
end
addinterpreter(f::Function, tile::Tile) = addinterpreter(InterpreterFn(f), tile)
interpret(dec::InterpreterFn, x) = dec.f(x)

# Apply a function with some constant partial args
# For efficiency, the function must be defined outside
# of any signal functions. i.e. no point creating
# ad-hoc functions

immutable InterpreterThunk <: Interpreter
    f::Function
    args::Tuple
end
partial(f::Function, args::Tuple, tile) =
    addinterpreter(InterpreterThunk(f, args), tile)

interpret(dec::InterpreterThunk, x) = dec.f(dec.args..., x)


name(d::WithInterpreter) = name(d.tile)

render(d::WithInterpreter, state) = render(d.tile, state)

#
# Subscribe to a signal and register an interpreter
#

immutable Subscription <: Tile
    tile::Tile
    name::Symbol # Name of the signal update on the front-end
    receiver::@compat Tuple{Interpreter, Input}
end

subscribe(t::Tile, name, s; absorb=true) =
    Subscription(t, name, s) |>
       (x -> absorb ? stoppropagation(x, name) : x)

subscribe(t::Behavior, s::Input; absorb=true) =
    subscribe(t, name(t), (default_interpreter(t), s), absorb=absorb)

subscribe(t::Behavior, s::(@compat Tuple{Interpreter, Input}); absorb=true) =
    subscribe(t, name(t), s, absorb=absorb)

subscribe(t::WithInterpreter, s::Input; absorb=true) =
    subscribe(t.tile, name(t), (t.interpreter, s), absorb=absorb)

render(sig::Subscription, state) =
    render(sig.tile, state) <<
        Elem("signal-transport",
            # Note: setup_transport here adds (interpreter, input) pair
            # to a dict, returns the key - this fn is idempotent
            name=sig.name, signalId=setup_transport(sig.receiver))

# Default definition of setup_transport
setup_transport(x) = makeid(x)

(>>>)(b::Behavior, s::Input) = subscribe(b, s)

### Sampling

@api sampler => Sampler <: Interpreter begin
    arg(triggers::Dict)
    arg(watched::Dict)
end
sampler() = Sampler(Dict(), Dict())

interpret(s::Sampler, msg) = begin
    try
        d = Dict()
        d[:_trigger] = symbol(msg["_trigger"])

{'fraction_non_alphanumeric': 0.1397238017871649, 'fraction_numerical': 0.0016246953696181965, 'mean_word_length': 4.679930795847751, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7420866', 'n_tokens_mistral': 3243, 'n_tokens_neox': 3321, 'n_words': 810}