diff --git a/clyngor/parsing.py b/clyngor/parsing.py index 535e747..0b36abb 100644 --- a/clyngor/parsing.py +++ b/clyngor/parsing.py @@ -33,8 +33,12 @@ def visit_number(self, node, children): def visit_args(self, node, children): return children - def visit_aloneargs(self, node, children): - return self.visit_term(node, ('', *children)) + def visit_single_value(self, node, children): + return self.visit_subterm(node, children) + def visit_one_uplet(self, node, children): + return self.visit_term(node, ('', children)) + def visit_n_uplet(self, node, children): + return self.visit_term(node, ('', children)) def visit_text(self, node, children): text = tuple(children)[0] if children else '' @@ -70,9 +74,12 @@ def ident(): return ap.RegExMatch(r'[a-z][a-zA-Z0-9_]*') def number(): return ap.RegExMatch(r'-?[0-9]+') def text(): return '"', ap.RegExMatch(r'((\\")|([^"]))*'), '"' def litteral(): return [text, number] - def subterm(): return [(ident, ap.Optional("(", args, ")")), litteral, aloneargs] - def args(): return subterm, ap.ZeroOrMore(',', subterm) - def aloneargs(): return '(', args, ')' + def subterm(): return [(ident, ap.Optional('(', args, ')')), litteral, aloneargs] + def args(): return subterm, ap.ZeroOrMore(',', subterm), ap.Optional(',') + def aloneargs(): return [single_value, one_uplet, n_uplet] + def single_value(): return '(', subterm, ')' + def one_uplet(): return '(', subterm, ',', ')' + def n_uplet(): return '(', subterm, ap.OneOrMore(',', subterm), ')' # NB: litteral outputed by #show are not handled. def term(): return ident, ap.Optional("(", args, ")") def terms(): return ap.ZeroOrMore(term) diff --git a/clyngor/test/test_parsing.py b/clyngor/test/test_parsing.py index 3cbaf30..4919e25 100644 --- a/clyngor/test/test_parsing.py +++ b/clyngor/test/test_parsing.py @@ -34,6 +34,13 @@ def test_parse_termset_default(): } assert Parser().parse_terms(string) == expected +def test_parse_tuple(): + string = 'a(b,(2,3,(a,b)))' + expected = { + ('a', ('b', ('', (2, 3, ('', ('a', 'b')))))), + } + assert Parser().parse_terms(string) == expected + def test_parse_args_without_predicat(): string = 'a((b,10)) c("",("",""))' expected = { diff --git a/clyngor/test/test_utils.py b/clyngor/test/test_utils.py index 1ebae5a..e5d08bb 100644 --- a/clyngor/test/test_utils.py +++ b/clyngor/test/test_utils.py @@ -60,15 +60,16 @@ def test_answer_set_to_str(): assert '.'.join(generate_answer_set_as_str((('a', (1, 2)), ('b', ())))) == 'a(1,2).b' assert ''.join(generate_answer_set_as_str((('a', (1, 2)), ('b', ())), atom_end='.')) == 'a(1,2).b.' assert '1'.join(generate_answer_set_as_str((('a', (1, 2)), ('b', ())), atom_end='2')) == 'a(1,2)21b2' - assert ' '.join(generate_answer_set_as_str((('', ('v', 2)), ('b', (['v', 3],))), atom_end='.')) == '(v,2). b((v,3)).' + assert ' '.join(generate_answer_set_as_str((('', ('v', 2)), ('b', (('', ['v', 3]),))), atom_end='.')) == '(v,2). b((v,3)).' assert ' '.join(generate_answer_set_as_str((('', ('', ('', (2,)))),))) == '("",(2,))' + assert '.'.join(generate_answer_set_as_str((('a', (('', ('1', '2')),)),))) == 'a((1,2))' def test_answer_set_to_str_complex(): generate_answer_set_as_str = utils.generate_answer_set_as_str asp = 'a(a(2,3),(2,),b(c((d,),(e,f)))).' - models = tuple(ASP(asp).careful_parsing) + models = tuple(ASP(asp).parse_args) print('CAREFUL:', models) + answerset = models[0] models = tuple(ASP(asp)) print('NORMAL :', models) - answerset = models[0] assert ' '.join(generate_answer_set_as_str(answerset, atom_end='.')) == asp diff --git a/clyngor/utils.py b/clyngor/utils.py index 59f9a6b..43adc67 100644 --- a/clyngor/utils.py +++ b/clyngor/utils.py @@ -112,7 +112,7 @@ def clingo_value_to_python(value:object) -> int or str or tuple: "".format(value, type(value))) -def python_value_to_asp(val:str or int or list or tuple, *, first_order:bool=True) -> str or tuple: +def python_value_to_asp(val:str or int or list or tuple, *, args_of_predicate:bool=False) -> str or tuple: """Convert given python value in an ASP format""" if isinstance(val, (str, int)): return str(val) or '""' @@ -122,14 +122,14 @@ def python_value_to_asp(val:str or int or list or tuple, *, first_order:bool=Tru assert len(val) == 2, "tuple value should be of size 2: (predicate, args))" predicate, args = val if args: - return predicate + python_value_to_asp(list(args), first_order=first_order) + return predicate + '(' + python_value_to_asp(list(args), args_of_predicate=bool(predicate)) + ')' else: # no args return predicate elif isinstance(val, list): - ender = ',)' if len(val) == 1 and not first_order else ')' - return '(' + ','.join(map(python_value_to_asp.second_order, val)) + ender + ender = ',' if len(val) == 1 and not args_of_predicate else '' + return ','.join(map(python_value_to_asp, val)) + ender raise ValueError(f"Python value '{repr(val)}' of type {type(val)} is not convertible in ASP.") -python_value_to_asp.second_order = lambda x: python_value_to_asp(x, first_order=False) +# python_value_to_asp.in_predicate = lambda x: python_value_to_asp(x, args_of_predicate=True) def answer_set_to_str(answer_set:iter, atom_end:str='', atom_sep:str=' ') -> str: