Current section
Files
Jump to
Current section
Files
src/bt_engine/yaml_parser/yaml_parse_subtree.erl
-module(yaml_parse_subtree).
-export([
parse/6
]).
-include("../bt_node_behaviours/bt_nodes.hrl").
throw_error_if_subtree_file_not_present(Ref, SubtreeName) ->
case Ref of
undefined ->
ErrorReason = "Could not find definition for subtree '" ++ SubtreeName ++ "'. Please include filename ref for it.",
erlang:error(ErrorReason);
_ -> ok
end.
throw_error_if_subtree_child_is_not_selector_or_sequence(SubtreeName, ChildName) ->
case ChildName of
"selector" -> ok;
"sequence" -> ok;
_ ->
ErrorReason = "Child of subtree '" ++ SubtreeName ++ "' is not a selector or sequence. Please fix.",
erlang:error(ErrorReason)
end.
% %% yaml dictionary is a map of subtree name => unparsed subtree yaml. this
% %% can be extended by reading more files.
-spec parse(
Subtree :: {string(), list({term(), term()})},
Namespace :: {bb_key, list(), term()},
ParentContext :: map(),
Blackboard :: map(),
OldYAMLDictionary :: term(),
FileDirectory :: string()
) -> {ParsedTree :: bt_node(), Blackboard :: blackboard()}.
parse(_SubTree = {SubtreeName, TempList}, Namespace, ParentContext, Blackboard, OldYAMLDictionary, FileDirectory) ->
%% check if definition in YAML dictionary, otherwise read from file
{YAMLDictionary, NewFileDirectory} = case maps:is_key(SubtreeName, OldYAMLDictionary) of
true -> {OldYAMLDictionary, FileDirectory};
false ->
Ref = proplists:get_value("ref", TempList),
%% throw error if ref doesn't exist
throw_error_if_subtree_file_not_present(Ref, SubtreeName),
FileName = filename:join(FileDirectory, Ref),
Directory = filename:dirname(FileName),
{maps:merge(yaml_parser_utils:read_file(FileName), OldYAMLDictionary), Directory}
end,
Decorators = proplists:get_value("decorators", TempList, []),
%% again assume that args and params are matching?
Args = proplists:get_value("args", TempList, []),
Name = proplists:get_value("name", TempList, SubtreeName),
List = maps:get(SubtreeName, YAMLDictionary),
ListWithArgs = List ++ [{"args", Args}],
ListWithNameAlso = ListWithArgs ++ [{"name", Name}],
parse({SubtreeName, ListWithNameAlso}, Decorators, Namespace, ParentContext, Blackboard, YAMLDictionary, NewFileDirectory).
parse(_Subtree = {SubtreeName, List}, [], Namespace, ParentContext, Blackboard, YAMLDictionary, FileDirectory) ->
Params = proplists:get_value("params", List, []),
Args = proplists:get_value("args", List, []),
bt_parser_utils:throw_error_if_args_and_params_dont_match(SubtreeName, Args, Params),
%% parse args to replace things using context
ParsedArgsContext = yaml_parser_utils:parse_args(Args, ParentContext),
%% create new context consisting just of params + local bb keys
BBKeysList = proplists:get_value("bb_keys", List, []),
Context = yaml_parser_utils:get_context(ParsedArgsContext, Namespace, BBKeysList),
%% add own keys to bb
NewBlackboard = bt_parser_utils:get_blackboard(BBKeysList, Namespace, Blackboard),
[{OnlyChildName, OnlyChildList}] = proplists:get_value("definition", List),
throw_error_if_subtree_child_is_not_selector_or_sequence(SubtreeName, OnlyChildName),
Name = proplists:get_value("name", List, SubtreeName),
%% use same namespace since this child is only selector or sequence, no conflict of variable declaration.
%% adding name to the child list, this will be used for display.
bt_yaml_parser:parse({OnlyChildName, OnlyChildList ++ [{"name", Name}]}, Namespace, Context, NewBlackboard, YAMLDictionary, FileDirectory);
%% TODO: consolidate decorator parse logic; right repeated in parse task/sequence/subtree
parse(Subtree = {_SubtreeName, _List}, [Decorator | RestOfDecorators], Namespace, ParentContext, Blackboard, YAMLDictionary, FileDirectory) ->
ParsedDecorator = yaml_parser_utils:get_decorator(Decorator, ParentContext),
{Child, NewBlackboard} = parse(Subtree, RestOfDecorators, Namespace, ParentContext, Blackboard, YAMLDictionary, FileDirectory),
{
ParsedDecorator#decorator{
child = Child
},
NewBlackboard
}.