Commit 7caeb4b0 authored by Bjarno Oeyen's avatar Bjarno Oeyen

Adds marlon_0.ex again after maliciously removing it

parent 72b0b97e
# Marlon - Multi-Agent Reinforcement Learning On Networks
# Copyright (C) 2018 Smile-IT project
#
# This file is part of Marlon.
#
# Marlon is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Marlon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Marlon. If not, see http://www.gnu.org/licenses
defmodule Marlon_0 do
@moduledoc """
Deprecated version of Marlon's macros
(which uses a keyword list syntax)
"""
# Ensure you can `use` the macros defined in this module
defmacro __using__(_opts) do
quote do
import Marlon_0
end
end
################################
## defactor
################################
@doc """
Defines an actor. (An actor is a wrapper around a GenServer that automatically
generates the client-side API.)
defactor has the following keyword parameters:
:init An anonymous function that returns an actor's initial state
(equivalent to a GenServer's init function)
:x Handler for message x. The value of this parameter is a 2-element List
The first element is either :call, :cast or :info, to specify the handler type.
The second element is an anonymous function.
Its first parameter always is the actor's state.
Any other parameters are the message's parameters.
"""
defmacro defactor(name, properties) do
{raw_contract, properties} = Keyword.pop(properties, :contract)
[fn_names, genserver_names] = categorize_members(properties)
contract = Marlon.Contract.build_contract(raw_contract, genserver_names)
# Generate client API fns
client_fns = for fn_name <- genserver_names do
[fn_type, fn_ast] = properties[fn_name]
genclient_fn(fn_name, fn_type, fn_ast)
end
# Generate corresponding server handlers
server_fns = for fn_name <- genserver_names do
[fn_type, fn_ast] = properties[fn_name]
genserver_fn(fn_name, fn_type, fn_ast)
end
# Generate plain functions
fns = for fn_name <- fn_names do
fn_args = get_fn_params(properties[fn_name])
fn_body = get_fn_body(properties[fn_name])
quote do
def unquote(fn_name)(unquote_splicing(fn_args)) do
unquote(fn_body)
end
end
end
# Put it all together
quote do
defmodule unquote(name) do
use GenServer
def start_link(state, opts) do
GenServer.start_link(__MODULE__, state, opts)
end
defp check_contract(msg) do
Marlon.Contract.check_contract(msg, unquote(contract))
end
unquote_splicing(fns)
unquote_splicing(Enum.filter(client_fns, & !is_nil(&1)))
use Marlon.Actor # Add agent-related functions right here
unquote_splicing(server_fns)
end
end
end
# Find out which of the members are plain functions, and which are GenServer handlers
# Returns [fn_names, genserver_names],
# where fn_names is a list of member names of plain functions
# and genserver_names is a list of member names of GenServer handlers
defp categorize_members(properties) do
member_names = Keyword.keys(properties)
Enum.reduce(member_names, [[], []],
fn(name, [fn_names, genserver_names]) ->
if (is_list(properties[name])) do
[fn_names, genserver_names ++ [name]]
else
[fn_names ++ [name] , genserver_names]
end
end)
end
# Generate a Genserver server-side function
# fn_type is :call, :cast or :info
defp genserver_fn(fn_name, fn_type, fn_ast) do
args = get_fn_params(fn_ast)
{fixed_args, other_args} = if (fn_type == :call) do
Enum.split(args, 2) # Call handlers have two fixed params; the state and sender
else
Enum.split(args, 1) # Cast and info handlers only have state
end
body = get_fn_body(fn_ast)
handle_fn_name = String.to_atom("handle"<>"_"<>Atom.to_string(fn_type))
quote do
def unquote(handle_fn_name)({unquote(fn_name),unquote_splicing(other_args)},unquote_splicing(Enum.reverse(fixed_args))) do
check_contract(unquote(fn_name))
unquote(body)
end
end
end
# Generate a Genserver client-side function
defp genclient_fn(fn_name, fn_type, fn_ast) do
args = get_fn_params(fn_ast)
{_fixed_args, other_args} = if (fn_type == :call) do
Enum.split(args, 2) # Call handlers have two fixed params; the state and sender
else
Enum.split(args, 1) # Cast and info handlers only have state
end
if fn_type == :info do
nil
else
quote do
def unquote(fn_name)(pid, unquote_splicing(other_args)) do
GenServer.unquote(fn_type)(pid,{unquote(fn_name), unquote_splicing(other_args)})
end
end
end
end
# Given an AST node of an anonymous function, get its list of formal parameters
defp get_fn_params(fn_ast) do
List.first(elem(List.first(elem(fn_ast,2)),2))
end
# Given an AST node of an anonymous function, get its function body
defp get_fn_body(fn_ast) do
List.last(elem(List.first(elem(fn_ast,2)),2))
end
################################
## defgoal
################################
defmacro defgoal(name, properties) do
# module = gen_module(properties[:type])
module = properties[:type]
quote do
defmodule unquote(name) do
import unquote(module)
def instantiate() do
Marlon.LearningAlgorithm.init_algorithm(%unquote(module){}, unquote(properties))
end
end
end
end
# Generates a module name: Marlon.XGoal
# def gen_module(name) do
# atom = String.to_atom(name <> "Goal")
# {:__aliases__, [alias: false], [:Marlon, atom]}
# end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment