Skip to content

Commit

Permalink
Implement reification/reflection of Comment AST. Minor bugfixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
namcsi committed Jun 14, 2024
1 parent fa4b98d commit a98ca71
Show file tree
Hide file tree
Showing 21 changed files with 240 additions and 135 deletions.
5 changes: 2 additions & 3 deletions src/renopro/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
The main entry point for the application.
"""

from renopro.rast import ReifiedAST

from .utils.logger import setup_logger
Expand Down Expand Up @@ -35,9 +36,7 @@ def main():
elif args.input_format == "reflected":
rast.reify_files(args.infiles)
for meta_encoding in args.meta_encodings:
rast.transform(
meta_files=meta_encoding, clingo_options=args.clingo_options
)
rast.transform(meta_files=meta_encoding, clingo_options=args.clingo_options)
if args.output_format == "reified":
print(rast.reified_string)
elif args.output_format == "reflected":
Expand Down
1 change: 1 addition & 0 deletions src/renopro/asp/ast.lp
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ ast(fact(theory_term_definitions(X0,X1,X2,X3))) :- theory_term_definitions(X0,X1
ast(fact(theory_guard_definition(X0,X1,X2))) :- theory_guard_definition(X0,X1,X2).
ast(fact(theory_atom_definitions(X0,X1,X2,X3,X4,X5,X6))) :- theory_atom_definitions(X0,X1,X2,X3,X4,X5,X6).
ast(fact(theory_definition(X0,X1,X2,X3))) :- theory_definition(X0,X1,X2,X3).
ast(fact(comment(X0,X1,X2))) :- comment(X0,X1,X2).
ast(fact(location(X0,X1,X2))) :- location(X0,X1,X2).
ast(fact(child(X0,X1))) :- child(X0,X1).
3 changes: 3 additions & 0 deletions src/renopro/asp/ast_fact2id.lp
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,7 @@ ast_fact2id(theory_atom_definitions(X0,X1,X2,X3,X4,X5,X6),theory_atom_definition
ast_fact2id(theory_definition(X0,X1,X2,X3),theory_definition(X0))
:- ast(fact(theory_definition(X0,X1,X2,X3));add(theory_definition(X0,X1,X2,X3));delete(theory_definition(X0,X1,X2,X3))).

ast_fact2id(comment(X0,X1,X2),comment(X0))
:- ast(fact(comment(X0,X1,X2));add(comment(X0,X1,X2));delete(comment(X0,X1,X2))).

ast_fact2id(location(Id,Begin,End),Id) :- ast(fact(location(Id,Begin,End));add(location(Id,Begin,End));delete(location(Id,Begin,End))).
1 change: 1 addition & 0 deletions src/renopro/asp/defined.lp
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@
#defined theory_guard_definition/3.
#defined theory_atom_definitions/7.
#defined theory_definition/4.
#defined comment/3.
#defined location/3.
#defined child/2.
14 changes: 13 additions & 1 deletion src/renopro/enum_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import enum
import inspect
from types import new_class
from typing import Type, TypeVar, Any
from typing import Any, Type, TypeVar

from clingo.ast import CommentType
from clorm import BaseField, ConstantField, StringField


Expand Down Expand Up @@ -218,3 +219,14 @@ class TheoryAtomType(str, enum.Enum):
TheoryAtomTypeField = define_enum_field(
parent_field=ConstantField, enum_class=TheoryAtomType, name="TheoryAtomTypeField"
)


class CommentType(str, enum.Enum):
"String enum of clingo's comment types."
Line = "line"
Block = "block"


CommentTypeField = define_enum_field(
parent_field=StringField, enum_class=CommentType, name="CommentTypeField"
)
39 changes: 30 additions & 9 deletions src/renopro/predicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import re
from itertools import count
from types import new_class
from typing import TYPE_CHECKING, Any, Protocol, Sequence, Type, Union, cast
from typing import TYPE_CHECKING, Any, Protocol, Sequence, Type, Union

import clingo
from clingo import Symbol
from clorm import (
BaseField,
Expand All @@ -22,6 +23,7 @@
from renopro.enum_fields import (
AggregateFunctionField,
BinaryOperatorField,
CommentTypeField,
ComparisonOperatorField,
SignField,
TheoryAtomTypeField,
Expand Down Expand Up @@ -84,7 +86,7 @@ def body(ns: dict[str, Any]) -> None:
# by default we use integer identifiers, but allow arbitrary symbols as well
# for flexibility when these are user generated
IntegerOrRawField = combine_fields([IntegerField, RawField], name="IntegerOrRawField")
IntegerOrRawField = IntegerOrRawField(default=lambda: next(id_count)) # type: ignore
IntegerOrRawField = IntegerOrRawField(default_factory=lambda: clingo.Number(next(id_count))) # type: ignore

# Metaclass shenanigans to dynamically create unary versions of AST predicates,
# which are used to identify child AST facts
Expand Down Expand Up @@ -127,8 +129,7 @@ class IdentifierPredicate(Predicate):


class IdentifierPredicateConstructor(Protocol):
def __call__(self, id: Any = ..., /) -> IdentifierPredicate:
...
def __call__(self, id: Any = ..., /) -> IdentifierPredicate: ...


class AstPredicate(Predicate, metaclass=_AstPredicateMeta):
Expand Down Expand Up @@ -263,7 +264,6 @@ class ExternalFunction(AstPredicate):


class Pool(AstPredicate):

"""Predicate representing a pool of terms.
id: Identifier of the pool.
Expand Down Expand Up @@ -454,7 +454,9 @@ class BooleanConstant(AstPredicate):
# have a pool argument.


FunctionOrPoolField = combine_fields([Function.unary.Field, Pool.unary.Field], name="FunctionOrPoolField")
FunctionOrPoolField = combine_fields(
[Function.unary.Field, Pool.unary.Field], name="FunctionOrPoolField"
)


class SymbolicAtom(AstPredicate):
Expand Down Expand Up @@ -1071,6 +1073,18 @@ class TheoryDefinition(AstPredicate):
atoms = TheoryAtomDefinitions.unary.Field


class Comment(AstPredicate):
"""Predicate representing a comment statement.
id: The identifier of the comment statement.
value: The string value the comment comprises of.
comment_type: The type of the comment, "block" or "line"."""

id = IntegerOrRawField
value = StringField
comment_type = CommentTypeField


StatementField.fields.extend(
[
External.unary.Field,
Expand All @@ -1079,6 +1093,7 @@ class TheoryDefinition(AstPredicate):
ProjectAtom.unary.Field,
ProjectSignature.unary.Field,
TheoryDefinition.unary.Field,
Comment.unary.Field,
]
)

Expand Down Expand Up @@ -1140,11 +1155,14 @@ class TheoryDefinition(AstPredicate):
TheoryTermDefinitions,
TheoryGuardDefinition,
TheoryAtomDefinitions,
TheoryDefinition
TheoryDefinition,
Comment,
)


AstIdentifierTermField = combine_fields([pred.unary.Field for pred in AstPreds], name="AstIdentifierTermField")
AstIdentifierTermField = combine_fields(
[pred.unary.Field for pred in AstPreds], name="AstIdentifierTermField"
)


class Position(ComplexTerm):
Expand All @@ -1171,6 +1189,7 @@ class Child(Predicate):
parent = AstIdentifierTermField
child = AstIdentifierTermField


AstPreds = AstPreds + (Location, Child)

AstPred = Union[
Expand Down Expand Up @@ -1230,9 +1249,10 @@ class Child(Predicate):
TheoryTermDefinitions,
TheoryGuardDefinition,
TheoryAtomDefinitions,
Comment,
TheoryDefinition,
Location,
Child
Child,
]


Expand All @@ -1250,6 +1270,7 @@ class Child(Predicate):
ProjectAtom,
ProjectSignature,
TheoryDefinition,
Comment,
)

FlattenedTuples = (
Expand Down
24 changes: 18 additions & 6 deletions src/renopro/rast.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
Callable,
Dict,
Iterator,
List,
Literal,
Optional,
Sequence,
Tuple,
Type,
Union,
overload,
cast,
List,
overload,
)

from clingo import Control, ast, symbol
Expand Down Expand Up @@ -167,7 +167,10 @@ class ReifiedAST:
def __init__(self) -> None:
self._reified = FactBase()
self._program_ast: List[AST] = []
self._current_statement: Tuple[Optional[preds.IdentifierPredicate], int] = (None, 0)
self._current_statement: Tuple[Optional[preds.IdentifierPredicate], int] = (
None,
0,
)
self._tuple_pos: Iterator[int] = count()
self._init_overrides()
self._parent_id_term: Optional[preds.IdentifierPredicate] = None
Expand Down Expand Up @@ -771,6 +774,13 @@ def reflect_fact(self, fact: preds.AstPred) -> AST: # nocoverage
elif clorm_enum := getattr(field, "enum", None):
ast_enum = getattr(ast, clorm_enum.__name__)
ast_enum_member = convert_enum(field_val, ast_enum)
# temporary fix due to bug in clingo ast CommentType
# https://github.com/potassco/clingo/issues/506
if ast_enum == ast.CommentType:
if ast_enum_member == ast.CommentType.Block:
ast_enum_member = 1
elif ast_enum_member == ast.CommentType.Line:
ast_enum_member = 0
kwargs_dict.update({key: ast_enum_member})
elif child_type in [str, int]:
kwargs_dict.update({key: field_val})
Expand Down Expand Up @@ -883,9 +893,11 @@ def transform(
)
level = log_lvl_str2int[log_lvl_symb.string]
log_strings = [
location_symb2str(s)
if s.match("location", 3)
else str(s).strip('"')
(
location_symb2str(s)
if s.match("location", 3)
else str(s).strip('"')
)
for s in symb.arguments[2:]
]
msg_str = msg_format_str.format(*log_strings)
Expand Down
1 change: 1 addition & 0 deletions src/renopro/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Utilities.
"""

from typing import NoReturn


Expand Down
39 changes: 25 additions & 14 deletions src/renopro/utils/codegen.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# nocoverage
from pathlib import Path
from typing import Any

import renopro.predicates as preds


def generate_replace():
def is_child_field(field: Any) -> bool:
"Check if field identifies a child predicate."
# Only complex or combined fields which are not IntegerOrRawField
# identify child predicates
return field.complex is not None or (
hasattr(field, "fields") and not field is preds.IntegerOrRawField
)


def generate_replace() -> None:
"Generate replacement rules from predicates."
program = (
"% replace(A,B) replaces a child predicate identifier A\n"
Expand All @@ -24,7 +34,7 @@ def generate_replace():
# we only care about replacing and combined
# fields - these are the terms that identify a child
# predicate
if field.complex is not None or hasattr(field, "fields"):
if is_child_field(field):
replacements.append(idx)
for idx in replacements:
old_args = ",".join(
Expand All @@ -39,10 +49,10 @@ def generate_replace():
f"ast(_replace_id(A,B)), ast(fact({name}({old_args}));add({name}({old_args}))).\n\n"
)
program += "ast(add(child(X0,B));delete(child(X0,A)))\n :- ast(_replace_id(A,B)), ast(fact(child(X0,A));add(child(X0,A)))."
Path("src", "renopro", "asp", "replace_id.lp").write_text(program)
Path("src", "renopro", "asp", "replace_id.lp").write_text(program, encoding="utf-8")


def generate_add_child():
def generate_add_child() -> None:
"""Generate rules to create child relations for all facts added
via ast add, and rules to replace identifiers via
ast replace."""
Expand All @@ -57,9 +67,7 @@ def generate_add_child():
if key == "id":
continue
field = getattr(predicate, key).meta.field
# we only care about complex adn combined fields - these
# are the terms that identify a child predicate
if field.complex is not None or hasattr(field, "fields"):
if is_child_field(field):
child_arg_indices.append(idx)
for idx in child_arg_indices:
add_child_args = ",".join(
Expand All @@ -69,10 +77,12 @@ def generate_add_child():
f"ast(add(child({name}(X0),Child)))\n :- "
f"ast(add({name}({add_child_args}))).\n\n"
)
Path("src", "renopro", "asp", "add-children.lp").write_text(add_child_program)
Path("src", "renopro", "asp", "add-children.lp").write_text(
add_child_program, encoding="utf-8"
)


def generate_ast_fact2id():
def generate_ast_fact2id() -> None:
"Generate rules mapping AST facts to their identifiers"
program = "% map ast facts to their identifiers.\n\n"
for predicate in preds.AstPreds:
Expand All @@ -88,10 +98,11 @@ def generate_ast_fact2id():
fact = f"{name}({args})"
identifier = f"{name}(X0)"
program += f"ast_fact2id({fact},{identifier})\n :- ast(fact({fact});add({fact});delete({fact})).\n\n"
Path("src", "renopro", "asp", "ast_fact2id.lp").write_text(program)
path = Path("src", "renopro", "asp", "ast_fact2id.lp")
path.write_text(program, encoding="utf-8")


def generate_ast():
def generate_ast() -> None:
"Generate rules to tag AST facts."
program = "% Rules to tag AST facts.\n\n"
for predicate in preds.AstPreds:
Expand All @@ -101,18 +112,18 @@ def generate_ast():
fact = f"{name}({args})"
rule = f"ast(fact({fact})) :- {fact}.\n"
program += rule
Path("src", "renopro", "asp", "ast.lp").write_text(program)
Path("src", "renopro", "asp", "ast.lp").write_text(program, encoding="utf-8")


def generate_defined():
def generate_defined() -> None:
"Generate defined statements for AST facts."
program = "% Defined statements for AST facts.\n\n"
for predicate in preds.AstPreds:
name = predicate.meta.name
arity = predicate.meta.arity
statement = f"#defined {name}/{arity}.\n"
program += statement
Path("src", "renopro", "asp", "defined.lp").write_text(program)
Path("src", "renopro", "asp", "defined.lp").write_text(program, encoding="utf-8")


if __name__ == "__main__":
Expand Down
2 changes: 2 additions & 0 deletions tests/asp/reify_reflect/reflected/comment_block.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
%* This is a
block comment *%
1 change: 1 addition & 0 deletions tests/asp/reify_reflect/reflected/comment_line.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
% This is a line comment
3 changes: 3 additions & 0 deletions tests/asp/reify_reflect/reified/comment_block.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
program(0,"base",constants(1),statements(2)).
comment(3,"%* This is a\nblock comment *%","block").
statements(2,0,comment(3)).
3 changes: 3 additions & 0 deletions tests/asp/reify_reflect/reified/comment_line.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
program(0,"base",constants(1),statements(2)).
comment(3,"% This is a line comment","line").
statements(2,0,comment(3)).
1 change: 0 additions & 1 deletion tests/asp/transform/meta-telingo/inputs/telingo-input.lp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
%
&tel{ >? fail(X) } :- &tel { shoot(X) & < <? shoot(X) & <* unloaded(X) }.
&tel{ shoot(X) & unloaded(X) & > unloaded(X) & > > (unloaded(X) >? < shoot(X)) }
:- &tel{ initial & firearm(X) }.
Expand Down
Loading

0 comments on commit a98ca71

Please sign in to comment.