Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .dialyzer_ignore.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# OTP 28 Dialyzer is stricter with opaque types. Ecto.Multi internally uses
# MapSet which wraps the opaque :sets.set(), triggering false positives.
# Tracked in: https://github.com/elixir-ecto/ecto/issues/4707
# Remove after upgrading to an Elixir version that fully fixes this.
[
~r/call_without_opaque.*opaque term/
]
25 changes: 15 additions & 10 deletions lib/coloured_flow/definition/arc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,28 @@ defmodule ColouredFlow.Definition.Arc do

def build_expression(:p_to_t, code) do
with {:ok, expression} <- Expression.build(code) do
case validate_bind_exprs(expression.expr) do
[] ->
{:error, {[], "missing `bind` in expression", code}}

validations ->
case Enum.find(validations, &match?({:error, _reason}, &1)) do
nil -> {:ok, expression}
{:error, reason} -> {:error, reason}
end
end
validate_p_to_t_bindings(expression, code)
end
end

def build_expression(:t_to_p, code) do
Expression.build(code)
end

defp validate_p_to_t_bindings(expression, code) do
case validate_bind_exprs(expression.expr) do
[] -> {:error, {[], "missing `bind` in expression", code}}
validations -> first_binding_error(expression, validations)
end
end

defp first_binding_error(expression, validations) do
case Enum.find(validations, &match?({:error, _reason}, &1)) do
nil -> {:ok, expression}
{:error, reason} -> {:error, reason}
end
end

@spec build_expression!(orientation(), code :: binary() | nil) :: Expression.t()
def build_expression!(orientation, code) do
case build_expression(orientation, code) do
Expand Down
23 changes: 14 additions & 9 deletions lib/coloured_flow/enabled_binding_elements/binding.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,20 @@ defmodule ColouredFlow.EnabledBindingElements.Binding do
"""
@spec combine(bindings_list :: [[[binding()]]]) :: [[binding()]]
def combine(bindings_list) do
Enum.reduce(bindings_list, [[]], fn bindings, prevs ->
for prev <- prevs, binding <- bindings, reduce: [] do
acc ->
case get_conflicts(prev, binding) do
[] -> [Keyword.merge(prev, binding) | acc]
_other -> acc
end
end
end)
Enum.reduce(bindings_list, [[]], &combine_step/2)
end

defp combine_step(bindings, prevs) do
for prev <- prevs, binding <- bindings, reduce: [] do
acc -> merge_if_compatible(acc, prev, binding)
end
end

defp merge_if_compatible(acc, prev, binding) do
case get_conflicts(prev, binding) do
[] -> [Keyword.merge(prev, binding) | acc]
_other -> acc
end
end

@doc """
Expand Down
60 changes: 33 additions & 27 deletions lib/coloured_flow/enabled_binding_elements/computation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,37 +51,43 @@ defmodule ColouredFlow.EnabledBindingElements.Computation do
binding_combinations = Binding.combine(arc_bindings)

Enum.flat_map(binding_combinations, fn binding ->
inputs
|> Enum.reduce_while([], fn {arc, place}, acc ->
arc_binding = merge_constants(binding, arc, constants)

with(
{:ok, {coefficient, value}} <- eval_arc(arc, arc_binding),
colour_set = fetch_colour_set!(place.colour_set, cpnet),
of_type_context = build_of_type_context(cpnet),
{:ok, ^value} <- ColourSet.Of.of_type(value, colour_set.type, of_type_context),
guard_binding = merge_constants(binding, transition, constants),
{:ok, true} <- eval_transition_guard(transition, guard_binding),
marking = get_marking(place, markings),
tokens = MultiSet.duplicate(value, coefficient),
true <- MultiSet.include?(marking.tokens, tokens)
) do
{:cont, [%Marking{place: place.name, tokens: tokens} | acc]}
else
_other -> {:halt, :error}
end
end)
|> case do
:error ->
[]
build_binding_element(inputs, binding, transition, constants, markings, cpnet)
end)
end

to_consume ->
# binding here should not contain constants
[BindingElement.new(transition.name, binding, to_consume)]
end
defp build_binding_element(inputs, binding, transition, constants, markings, cpnet) do
case collect_consumption(inputs, binding, transition, constants, markings, cpnet) do
:error -> []
to_consume -> [BindingElement.new(transition.name, binding, to_consume)]
end
end

defp collect_consumption(inputs, binding, transition, constants, markings, cpnet) do
Enum.reduce_while(inputs, [], fn {arc, place}, acc ->
try_consume(arc, place, binding, transition, constants, markings, cpnet, acc)
end)
end

defp try_consume(arc, place, binding, transition, constants, markings, cpnet, acc) do
arc_binding = merge_constants(binding, arc, constants)

with(
{:ok, {coefficient, value}} <- eval_arc(arc, arc_binding),
colour_set = fetch_colour_set!(place.colour_set, cpnet),
of_type_context = build_of_type_context(cpnet),
{:ok, ^value} <- ColourSet.Of.of_type(value, colour_set.type, of_type_context),
guard_binding = merge_constants(binding, transition, constants),
{:ok, true} <- eval_transition_guard(transition, guard_binding),
marking = get_marking(place, markings),
tokens = MultiSet.duplicate(value, coefficient),
true <- MultiSet.include?(marking.tokens, tokens)
) do
{:cont, [%Marking{place: place.name, tokens: tokens} | acc]}
else
_other -> {:halt, :error}
end
end

@spec reject_invalid_bandings([[[BindingElement.binding()]]], ColouredPetriNet.t()) ::
[[[BindingElement.binding()]]]
defp reject_invalid_bandings(bindings_list, cpnet) do
Expand Down
2 changes: 1 addition & 1 deletion lib/coloured_flow/notation/colset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ defmodule ColouredFlow.Notation.Colset do
defp decompose_union([], acc) do
duplicate_tags = find_duplicate_tags(acc)

if length(duplicate_tags) > 0 do
if duplicate_tags != [] do
raise """
Invalid union tags, duplicate tags found: `#{type_to_string(duplicate_tags)}`
"""
Expand Down
6 changes: 4 additions & 2 deletions lib/coloured_flow/runner/enactment/workitem_calibration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ defmodule ColouredFlow.Runner.Enactment.WorkitemCalibration do
workitem_occurrences = Keyword.fetch!(options, :workitem_occurrences)

to_consume_markings =
Enum.flat_map(workitem_occurrences, fn {workitem, _} ->
Enum.flat_map(workitem_occurrences, fn {workitem, _occurrence} ->
workitem.binding_element.to_consume
end)

Expand Down Expand Up @@ -170,7 +170,9 @@ defmodule ColouredFlow.Runner.Enactment.WorkitemCalibration do
ColouredPetriNet.t()
) :: {enactment_state(), MultiSet.t(BindingElement.t())}
defp produce_workitems(%Enactment{} = state, workitem_occurrences, %ColouredPetriNet{} = cpnet) do
completed_workitem_ids = Enum.map(workitem_occurrences, fn {workitem, _} -> workitem.id end)
completed_workitem_ids =
Enum.map(workitem_occurrences, fn {workitem, _occurrence} -> workitem.id end)

occurrences = Enum.map(workitem_occurrences, &elem(&1, 1))

{steps, markings} =
Expand Down
31 changes: 11 additions & 20 deletions lib/coloured_flow/runner/storage/schemas/enactment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,20 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Enactment do

@type state() :: unquote(ColouredFlow.Types.make_sum_type(states))

typed_structor define_struct: false, enforce: true do
field :id, Types.id()
field :flow_id, Types.id()
field :flow, Types.association(Flow.t())

field :state, state(), default: :running
field :label, String.t(), enforce: false
field :initial_markings, [Marking.t()]
field :final_markings, [Marking.t()], enforce: false
typed_schema "enactments", null: false do
belongs_to :flow, Flow

field :steps, Types.association([Occurrence.t()])
field :state, Ecto.Enum,
values: [:running, :exception, :terminated],
default: :running,
typed: [type: state()]

field :inserted_at, DateTime.t()
field :updated_at, DateTime.t()
end

schema "enactments" do
belongs_to :flow, Flow
field :label, :string, typed: [null: true]
field :initial_markings, {:array, Object}, codec: Codec.Marking, typed: [type: [Marking.t()]]

field :state, Ecto.Enum, values: [:running, :exception, :terminated], default: :running
field :label, :string
field :initial_markings, {:array, Object}, codec: Codec.Marking
field :final_markings, {:array, Object}, codec: Codec.Marking
field :final_markings, {:array, Object},
codec: Codec.Marking,
typed: [type: [Marking.t()], null: true]

has_many :steps, Occurrence

Expand Down
43 changes: 19 additions & 24 deletions lib/coloured_flow/runner/storage/schemas/enactment_log.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,34 @@ defmodule ColouredFlow.Runner.Storage.Schemas.EnactmentLog do

alias ColouredFlow.Runner.Storage.Schemas.Enactment

typed_structor define_struct: false, enforce: true do
field :id, Types.id()
field :enactment_id, Types.id()
field :enactment, Types.association(Enactment.t())

field :state, Enactment.state()

field :termination,
%{type: ColouredFlow.Runner.Termination.type(), message: String.t() | nil},
enforce: false

field :exception, %{type: String.t(), message: String.t(), original: String.t()},
enforce: false

field :inserted_at, DateTime.t()
end

schema "enactment_logs" do
typed_schema "enactment_logs", null: false do
belongs_to :enactment, Enactment

field :state, Ecto.Enum, values: Enactment.__states__()
field :state, Ecto.Enum, values: Enactment.__states__(), typed: [type: Enactment.state()]

embeds_one :termination, Termination, primary_key: false, on_replace: :delete do
embeds_one :termination, Termination,
primary_key: false,
on_replace: :delete,
typed: [null: true] do
@moduledoc false

field :type, Ecto.Enum, values: ColouredFlow.Runner.Termination.__types__()
field :message, :string
field :type, Ecto.Enum,
values: ColouredFlow.Runner.Termination.__types__(),
typed: [type: ColouredFlow.Runner.Termination.type()]

field :message, :string, typed: [null: true]
end

embeds_one :exception, Exception, primary_key: false, on_replace: :delete do
embeds_one :exception, Exception,
primary_key: false,
on_replace: :delete,
typed: [null: true] do
@moduledoc false

field :reason, Ecto.Enum, values: ColouredFlow.Runner.Exception.__reasons__()
field :reason, Ecto.Enum,
values: ColouredFlow.Runner.Exception.__reasons__(),
typed: [type: ColouredFlow.Runner.Exception.reason()]

field :type, :string
field :message, :string
field :original, :string
Expand Down
12 changes: 2 additions & 10 deletions lib/coloured_flow/runner/storage/schemas/flow.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,9 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Flow do

alias ColouredFlow.Definition.ColouredPetriNet

typed_structor define_struct: false, enforce: true do
field :id, Types.id()
field :name, String.t()
field :definition, ColouredPetriNet.t()

field :inserted_at, DateTime.t()
end

schema "flows" do
typed_schema "flows", null: false do
field :name, :string
field :definition, Object, codec: Codec.ColouredPetriNet
field :definition, Object, codec: Codec.ColouredPetriNet, typed: [type: ColouredPetriNet.t()]

timestamps(updated_at: false)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ defmodule ColouredFlow.Runner.Storage.Schemas.JsonInstance.Codec.ColourSet do
Converts the `descr` to a JSON representation.
"""
@spec encode_descr(ColourSet.descr()) :: map()
def encode_descr({primitive, _}) when primitive in @primitive_types do
def encode_descr({primitive, _args}) when primitive in @primitive_types do
%{
"type" => Atom.to_string(primitive),
"args" => []
Expand Down
19 changes: 3 additions & 16 deletions lib/coloured_flow/runner/storage/schemas/occurrence.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,15 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Occurrence do

@type step_number() :: pos_integer()

typed_structor define_struct: false, enforce: true do
field :enactment_id, Types.id()
field :enactment, Types.association(Enactment.t())
field :step_number, step_number()

field :workitem_id, Types.id()
field :workitem, Types.association(Workitem.t())

field :occurrence, Occurrence.t()

field :inserted_at, DateTime.t()
end

@primary_key false

schema "occurrences" do
typed_schema "occurrences", null: false do
belongs_to :enactment, Enactment, primary_key: true
field :step_number, :integer, primary_key: true
field :step_number, :integer, primary_key: true, typed: [type: step_number()]

belongs_to :workitem, Workitem

field :occurrence, Object, codec: Codec.Occurrence
field :occurrence, Object, codec: Codec.Occurrence, typed: [type: Occurrence.t()]

timestamps(updated_at: false)
end
Expand Down
4 changes: 1 addition & 3 deletions lib/coloured_flow/runner/storage/schemas/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Schema do
quote generated: true do
alias ColouredFlow.Runner.Storage.Schemas.JsonInstance.Codec
alias ColouredFlow.Runner.Storage.Schemas.JsonInstance.Object
alias ColouredFlow.Runner.Storage.Schemas.Types

use Ecto.Schema
use TypedStructor
use EctoTypedSchema

@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
Expand Down
17 changes: 3 additions & 14 deletions lib/coloured_flow/runner/storage/schemas/snapshot.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,13 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Snapshot do
alias ColouredFlow.Runner.Enactment.Snapshot
alias ColouredFlow.Runner.Storage.Schemas.Enactment

typed_structor define_struct: false, enforce: true do
field :enactment_id, Types.id()
field :enactment, Types.association(Enactment.t())

field :version, pos_integer()
field :markings, [Marking.t()]

field :inserted_at, DateTime.t()
field :updated_at, DateTime.t()
end

@primary_key false

schema "snapshots" do
typed_schema "snapshots", null: false do
belongs_to :enactment, Enactment, primary_key: true

field :version, :integer
field :markings, {:array, Object}, codec: Codec.Marking
field :version, :integer, typed: [type: pos_integer()]
field :markings, {:array, Object}, codec: Codec.Marking, typed: [type: [Marking.t()]]

timestamps()
end
Expand Down
16 changes: 0 additions & 16 deletions lib/coloured_flow/runner/storage/schemas/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,4 @@ defmodule ColouredFlow.Runner.Storage.Schemas.Types do
The type for a primary_key or foreign_key, represented as a UUID.
"""
@type id() :: Ecto.UUID.t()

@typedoc """
Represents an optional UUID, which can be `nil`.
"""
@type maybe_id() :: ColouredFlow.Types.maybe(id())

@typedoc """
Represents an Ecto association, which can either be not loaded or of type `t`.
"""
@type association(t) :: Ecto.Association.NotLoaded.t() | t

@typedoc """
Represents an optional Ecto association, which can either be not loaded, of type
`t`, or `nil`.
"""
@type maybe_association(t) :: Ecto.Association.NotLoaded.t() | ColouredFlow.Types.maybe(t)
end
Loading
Loading