Skip to content

Commit

Permalink
Scope refactor (#1199)
Browse files Browse the repository at this point in the history
* scope implement context

* test scope

* implement scope refactor

* fix leave_scope

* fix create

* fix leave_context

* fix tests

* make init private
  • Loading branch information
herissondev committed Jul 31, 2023
1 parent f0c7660 commit 7efafb6
Show file tree
Hide file tree
Showing 6 changed files with 740 additions and 115 deletions.
21 changes: 5 additions & 16 deletions lib/archethic/contracts/interpreter/action_interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,18 @@ defmodule Archethic.Contracts.Interpreter.ActionInterpreter do

# Apply some transformations to the transactions
# We do it here because the Constants module is still used by InterpreterLegacy
constants =
constants
|> Constants.map_transactions(&Constants.stringify_transaction/1)
|> Constants.map_transactions(&Constants.cast_transaction_amount_to_float/1)

# we use the process dictionary to store our scope
# because it is mutable.
#
# constants should already contains the global variables:
# also, constants should already contains the global variables:
# - "contract": current contract transaction
# - "transaction": the incoming transaction (when trigger=transaction|oracle)
# - "_time_now": the time returned by Time.now()

Scope.init(
constants =
constants
|> Constants.map_transactions(&Constants.stringify_transaction/1)
|> Constants.map_transactions(&Constants.cast_transaction_amount_to_float/1)
|> Map.put("next_transaction", initial_next_tx)
|> Map.put("next_transaction_changed", false)
)

# we can ignore the result & binding
# - `result` would be the returned value of the AST
# - `binding` would be the variables (none since everything is written to the process dictionary)
{_result, _binding} = Code.eval_quoted(ast)
Scope.execute(ast, constants)

# return a next transaction only if it has been modified
if Scope.read_global(["next_transaction_changed"]) do
Expand Down
34 changes: 19 additions & 15 deletions lib/archethic/contracts/interpreter/common_interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,15 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
_node = {:__block__, meta, expressions},
acc
) do
# create a "ref" for each block
# references are not AST valid, so we convert them to binary
# (ps: charlist is a slow alternative because the Macro.traverse will step into every character)
ref = :erlang.list_to_binary(:erlang.ref_to_list(make_ref()))
new_acc = acc ++ [ref]

# create the child scope in parent scope
create_scope_ast =
quote do
Scope.create(unquote(new_acc))
Scope.create()
end

{
{:__block__, meta, [create_scope_ast | expressions]},
new_acc
acc
}
end

Expand Down Expand Up @@ -166,7 +160,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
) do
new_node =
quote do
Scope.write_cascade(unquote(acc), unquote(var_name), unquote(value))
Scope.write_cascade(unquote(var_name), unquote(value))
end

{
Expand All @@ -179,7 +173,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
def prewalk(_node = {{:., _, [{{:atom, map_name}, _, nil}, {:atom, key_name}]}, _, _}, acc) do
new_node =
quote do
Scope.read(unquote(acc), unquote(map_name), unquote(key_name))
Scope.read(unquote(map_name), unquote(key_name))
end

{new_node, acc}
Expand All @@ -205,7 +199,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
# accessor can be a variable, a function call, a dot access, a string
new_node =
quote do
Scope.read(unquote(acc), unquote(map_name), unquote(accessor))
Scope.read(unquote(map_name), unquote(accessor))
end

{new_node, acc}
Expand Down Expand Up @@ -276,12 +270,22 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
# |_|
# ----------------------------------------------------------------------
# exit block == set parent scope
# we need to return user's last expression and not the result of Scope.leave_scope()
def postwalk(
node = {:__block__, _, _},
_node = {:__block__, meta, expressions},
acc,
_
) do
{node, List.delete_at(acc, -1)}
{last_expression, expressions} = List.pop_at(expressions, -1)

{:__block__, _meta, new_expressions} =
quote do
result = unquote(last_expression)
Scope.leave_scope()
result
end

{{:__block__, meta, expressions ++ new_expressions}, acc}
end

# common modules call
Expand Down Expand Up @@ -334,7 +338,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
) do
new_node =
quote do
Scope.read(unquote(acc), unquote(var_name))
Scope.read(unquote(var_name))
end

{new_node, acc}
Expand All @@ -360,7 +364,7 @@ defmodule Archethic.Contracts.Interpreter.CommonInterpreter do
new_node =
quote do
Enum.each(unquote(list), fn x ->
Scope.write_at(unquote(acc), unquote(var_name), x)
Scope.write_at(unquote(var_name), x)

unquote(block)
end)
Expand Down
5 changes: 1 addition & 4 deletions lib/archethic/contracts/interpreter/condition_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,6 @@ defmodule Archethic.Contracts.Interpreter.ConditionValidator do

defp evaluate_condition(ast, constants) do
# reset scope and set constants
Scope.init(constants)

{result, _} = Code.eval_quoted(ast)
result
Scope.execute(ast, constants)
end
end
4 changes: 1 addition & 3 deletions lib/archethic/contracts/interpreter/function_interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ defmodule Archethic.Contracts.Interpreter.FunctionInterpreter do
"""
@spec execute(ast :: any(), constants :: map()) :: result :: any()
def execute(ast, constants) do
Scope.init(constants)
{result, _} = Code.eval_quoted(ast)
result
Scope.execute(ast, constants)
end

# ----------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 7efafb6

Please sign in to comment.