Source code for rafcontpp.logic.pddl_action_parser

# Copyright (C) 2018-2019 DLR
#
# All rights reserved. This program and the accompanying materials are made
# available under the terms of the 3-Clause BSD License which accompanies this
# distribution, and is available at
# https://opensource.org/licenses/BSD-3-Clause
#
# Contributors:
# Christoph Suerig <christoph.suerig@dlr.de>

# Don't connect with the Copyright comment above!
# Version: 17.05.2019
import re

from rafcon.utils import log

from rafcontpp.model.pddl_action_representation import PddlActionRepresentation

logger = log.get_logger(__name__)


[docs]class PddlActionParser: """ PddlActionParser PddlActionParser is able to parse an pddl action string e.g. a usual pddl action into a PddlActionRepresentation by extracting predicates, types, variables and the action name. """ def __init__(self, action_string): """ :param action_string: A pddl action string """ if action_string is None or len(action_string) == 0: logger.error('Can not parse action from None or Empty String!') raise ValueError('Can not parse action from None or Empty String!') # matches variables with types e.g ?obj - Physobj self.__type_var_pattern = re.compile('((\?[^\s|^\)|^\(]+\s+)+-\s+[^\s|^\)|^\(]+)') # matches only type strings result f.e. Location self.__type_pattern = re.compile('-\s+([^\s|^\)|^?|^-]+)') # matches only varialbes f.e. ?myVar self.__var_pattern = re.compile('\?[^\s|^\)]+') # matches applied predicates self.__predicate_pattern = re.compile('(\(\s*[^\?|^\s|^\(]+(\s+\?[^\)|^\(|^\s]*)+\s*\))') # matches the action name, ignores cases self.__action_name_pattern = re.compile('\(:action\s*([^\s|^:]+)', re.IGNORECASE) # a dictionary, which contains all variables and their types. self.__var_type_dict = {} # the pddl action as string, comments removed. self.__action_string = self.__clean_comments(action_string)
[docs] def parse_action(self): """ parse_action takes the given action string, and parses it into a PddlActionRepresenation, raises ValueError, if the action string is none or empty. :return: PddlActionRepresentation: A PddlActionRepresentation of the pddl action. """ self.__create_var_type_dict() name = self.parse_action_name() predicates = self.__parse_and_generalize_predicates() types = list(set(self.__var_type_dict.values())) parameters = self.parse_parameters() return PddlActionRepresentation(name, self.__action_string, predicates, types, [], parameters)
[docs] def parse_action_name(self): """ parse_action_name reads the action form the given action and returns it. :return: String: The name of the action. """ parsed = re.findall(self.__action_name_pattern, self.__action_string) if len(parsed) == 0: logger.error("Couldn't parse action name from \"{}\"".format(self.__action_string)) raise ValueError("Couldn't parse action name!") return parsed[0]
[docs] def parse_parameters(self): """ parse_parameters parses the parameters out of an pddl action string. :return: [String]: A list with parameter names, without '?' """ params = [] action_string_upper = self.__action_string.upper() if ':PARAMETERS' in action_string_upper: start_index = action_string_upper.index(':PARAMETERS') if ')' in self.__action_string[start_index:]: end_index = start_index + self.__action_string[start_index:].index(')') type_vars = self.__type_var_pattern.findall(self.__action_string[start_index:end_index]) for type_var in type_vars: for var in self.__var_pattern.findall(type_var[0]): params.append(var.strip('?')) else: logger.error('No Parameters found in: ' + self.__action_string) raise ValueError('No Parameters found in: ' + self.__action_string) return params
def __clean_comments(self, action_string): """ Takes the action string and removes all comments. :return: String: The action string without comments. """ comment_pattern = re.compile('(;[^\n]*)') return comment_pattern.sub('', action_string) def __create_var_type_dict(self): """ create_var_type_dict creates a dictionary, which contains all variables with their type, defined in the action. :return: {String:String}: A dictionary containing variable : type pairs. """ type_vars = self.__type_var_pattern.findall(self.__action_string) for type_var in type_vars: types = self.__type_pattern.findall(type_var[0]) type = types[0] for var in self.__var_pattern.findall(type_var[0]): self.__var_type_dict[var] = type return self.__var_type_dict def __parse_and_generalize_predicates(self): """ This method extracts all applied pradicates from the action, then it generalizes them. e.g. add types to the variables and remove dublicats. applied predicate example: (at ?a ?b) generalized predicate example: (at ?a - Location ?b - Robot) :return: [String]: A list with all parsed predicates. """ # matches applied predicates a_pred_name_pattern = re.compile('\(([^\s]+)\s') applied_predicates = [i[0] for i in re.findall(self.__predicate_pattern, self.__action_string)] if not self.__var_type_dict: self.__create_var_type_dict() # change applied predicates to normal ones parsed_predicates = {} # iterate through all used predicates for applied_predicate in applied_predicates: generalized_predicate = '(' c_pred_name = a_pred_name_pattern.findall(applied_predicate)[0] if not self.__is_built_in_pred(c_pred_name): c_pred_vars = self.__var_pattern.findall(applied_predicate) generalized_predicate += c_pred_name # the last type used last_type = '' # a concatination of all types, used to produce an identifier for the predicate type_concat = '' # iterate through all variables of the predicate for c_pred_var in c_pred_vars: if c_pred_var in self.__var_type_dict: c_type = self.__var_type_dict[c_pred_var] if last_type == '': last_type = c_type type_concat += c_type if last_type != c_type: generalized_predicate += ' - ' + last_type last_type = c_type type_concat += c_type generalized_predicate += ' ' + c_pred_var else: logger.error('Variable: ' + c_pred_var + ' not defined!') raise ValueError('Variable: ' + c_pred_var + ' not defined!') generalized_predicate += ' - ' + last_type + ')' # add predicate to a dictionary, to eliminate duplicats. # two predicates with the same name, but different types are handled as two # predicates at this time. Because of unknowen type hierarchies, its not decidable # at this time how to merge the predicates. parsed_predicates[c_pred_name + type_concat] = generalized_predicate return parsed_predicates.values() def __is_built_in_pred(self, name): """ Checks if the Predicate is a PDDL built-in predicate. :param name: The name of a predicate. :return: Boolean: True if the predicate is a PDDL built-in predicate, false otherwise. """ is_built_in = False if name: is_built_in = True if name == '=' else is_built_in return is_built_in