# Copyright (C) 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
logger = log.get_logger(__name__)
[docs]class PddlFactsParser:
def __init__(self, facts_string):
"""
The facts file parser will later be used to parse a raw facts file into a pddl facts file representation.
:param facts_string: The facts file content as string.
"""
if not facts_string:
logger.error('facts_string can\'t be None!')
raise ValueError('facts_string can\'t be None!')
self.__facts_string = self.__clean_comments(facts_string)
# matches the whole objects section, without (:objects and )
self.__objects_section_pattern = re.compile('\(:objects([^\)]+)\)', re.IGNORECASE | re.MULTILINE)
self.__objects_type_pattern = re.compile('([^-]+\s+-\s+[^\s]+)')
# matches the domain_name in a facts file
self.__domain_name_pattern = re.compile('\(\s*:domain\s+([^\s|^\)]+)', re.IGNORECASE)
# machtes the problem name in a facts file
self.__problem_name_pattern = re.compile('\(\s*problem\s+([^\s|^\)]+)', re.IGNORECASE)
[docs] def parse_objects(self):
"""
Parse object parses the Objects present in the facts file, and returns a map of all objects with their type,
in format: object: type
:return: {String:String}: A map of format: key: Object, value: type.
"""
obj_type_map = {}
obj_sec_tup = self.__objects_section_pattern.findall(self.__facts_string)
if obj_sec_tup and len(obj_sec_tup) == 1:
obj_sec = obj_sec_tup[0] # a string containing the object section
obj_types = self.__objects_type_pattern.findall(obj_sec) # a list with all object - type patterns
for obj_type in obj_types:
obj_type_list = obj_type.split() # split list
obj_type_list.remove('-')
c_type = obj_type_list[
len(obj_type_list) - 1] # type is always the last item in list (because of the match)
obj_list = list(obj_type_list)
obj_list.pop() # remove the type, to get only the objects in the list.
for obj in obj_list:
if obj in obj_type_map.keys() and c_type != obj_type_map[obj]:
logger.error(
'Object {0:} assigned as type {1:} and type {2:}.'.format(obj, c_type, obj_type_map[obj]))
raise ValueError(
'Object {0:} assigned as type {1:} and type {2:}.'.format(obj, c_type, obj_type_map[obj]))
else:
obj_type_map[obj] = c_type
else:
logger.error('No Objects found in facts file!')
raise ValueError()
return obj_type_map
[docs] def parse_domain_name(self):
"""
parse_domain_name parses the domain name out of the given facts file.
:return: String: The domain name.
"""
parsed = self.__domain_name_pattern.findall(self.__facts_string)
if len(parsed) == 0:
logger.error("Couldn't parse domain name from facts file!")
raise ValueError("Couldn't parse domain name!")
return parsed[0]
[docs] def parse_problem_name(self):
"""
parse_problem_name parses the problem name out of the given facts file.
:return: String: The problem name.
"""
parsed = self.__problem_name_pattern.findall(self.__facts_string)
if len(parsed) == 0:
logger.error("Couldn't parse problem name from facts file!")
raise ValueError("Couldn't parse problem name!")
return parsed[0]
def __clean_comments(self, facts_string):
"""
Takes the facts string and removes all comments.
:return: String: The facts string without comments.
"""
comment_pattern = re.compile('(;[^\n]*)')
return comment_pattern.sub('', facts_string)