-module(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(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, []), parser_utils:throw_error_if_args_and_params_dont_match(SubtreeName, Args, Params), %% parse args to replace things using context ParsedArgsContext = parser_utils:parse_args(Args, ParentContext), %% create new context consisting just of params + local bb keys BBKeysList = proplists:get_value("bb_keys", List, []), Context = parser_utils:get_context(ParsedArgsContext, Namespace, BBKeysList), %% add own keys to bb NewBlackboard = parser_utils:get_blackboard(BBKeysList, Namespace, Context, 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_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 = parser_utils:get_decorator(Decorator, ParentContext), {Child, NewBlackboard} = parse(Subtree, RestOfDecorators, Namespace, ParentContext, Blackboard, YAMLDictionary, FileDirectory), { ParsedDecorator#decorator{ child = Child }, NewBlackboard }.