diff --git a/movie-example/shapes.progs b/movie-example/shapes.progs index 0146bd7..03ff3fd 100644 --- a/movie-example/shapes.progs +++ b/movie-example/shapes.progs @@ -6,7 +6,7 @@ NODE movieShape [:movie] { NODE actorShape [BOTTOM] { :person & >=1 :acted_in.movieShape & - >=1 name.string + =1 name.string }; EDGE actedInShape [:acted_in] { @@ -17,7 +17,7 @@ EDGE actedInShape [:acted_in] { NODE directorShape [BOTTOM] { :person & >=1 :directed.movieShape & - >=1 name.string + =1 name.string }; EDGE directedShape [:directed] { @@ -28,7 +28,7 @@ EDGE directedShape [:directed] { NODE producerShape [BOTTOM] { :person & >=1 :produced.movieShape & - >=1 name.string + =1 name.string }; EDGE producedShape [:produced] { @@ -39,10 +39,10 @@ EDGE producedShape [:produced] { NODE writerShape [BOTTOM] { :person & >=1 :wrote.movieShape & - >=1 name.string + =1 name.string }; EDGE wroteShape [:wrote] { << writerShape & >> movieShape -}; \ No newline at end of file +}; diff --git a/paper-example/graph.lp b/paper-example/graph.lp index 74f513e..847fc31 100644 --- a/paper-example/graph.lp +++ b/paper-example/graph.lp @@ -29,4 +29,4 @@ property(101, name, string("Wernham Hogg")). property(102, name, string("Gareth Keenan")). property(203, since, integer(2020)). property(200, since, integer(1970)). -property(200, since, integer(2007)). +property(200, since, integer(2007)). \ No newline at end of file diff --git a/paper-example/shapes.progs b/paper-example/shapes.progs index b5a354f..f18c82b 100644 --- a/paper-example/shapes.progs +++ b/paper-example/shapes.progs @@ -1,8 +1,7 @@ NODE employeeShape [:employee] { - :employee & - >= 1 :worksFor.organizationShape + >= 1 worksForShape }; -NODE organizationShape [BOTTOM] { - :company -}; +EDGE worksForShape [BOTTOM] { + :worksFor +}; \ No newline at end of file diff --git a/scripts/translate.py b/scripts/graph-encoder.py similarity index 100% rename from scripts/translate.py rename to scripts/graph-encoder.py diff --git a/scripts/shapeTranslator.py b/scripts/shape-transpiler.py similarity index 87% rename from scripts/shapeTranslator.py rename to scripts/shape-transpiler.py index 43d72b1..d4df176 100755 --- a/scripts/shapeTranslator.py +++ b/scripts/shape-transpiler.py @@ -5,6 +5,7 @@ from lark import Lark, Transformer constraint_store = set() +path_store = set() class ShapeTransformer(Transformer): @@ -30,6 +31,33 @@ def bot(self, b): def property(self, k): return "hasProperty({})".format(k[0]) + def shaperef(self, s): + c = 'shapeRef({})'.format(s[0]) + constraint_store.add(c) + return c + + # Paths + + def concatpath(self, ps): + p = 'concatpath({},{})'.format(ps[0],ps[1]) + path_store.add(p) + return p + + def negatepath(self, ps): + p = 'negatepath({})'.format(ps[0]) + path_store.add(p) + return p + + def choicepath(self, ps): + p = 'choicepath({},{})'.format(ps[0],ps[1]) + path_store.add(p) + return p + + def repeatpath(self, ps): + p = 'repeatpath({})'.format(ps[0]) + path_store.add(p) + return p + # Node Constraint def nodeconstraint_basic(self, items): @@ -61,7 +89,9 @@ def negate(self, n): return c def path(self, p): - return p[0] + p = p[0] + path_store.add(p) + return p def greatereq(self, p): dat = p[0].data @@ -219,8 +249,18 @@ def WORD(self, word): def NUMBER(self, nr): return int(nr) + +def print_paths(): + paths = list(path_store) + paths.sort() + for p in paths: + print('path({}).'.format(p)) + + def print_constraints(): - for c in constraint_store: + constraints = list(constraint_store) + constraints.sort() + for c in constraints: print('constraint({}).'.format(c)) @@ -245,13 +285,15 @@ def main(argv): shapes = '' with open(infile, 'r') as file: - shapes = file.read() + shapes = file.read() + '\n' shape_parser = Lark(grammar, start='shapes') tree = shape_parser.parse(shapes) transformed = ShapeTransformer().transform(tree) + print_constraints() + print_paths() print(transformed) if __name__ == '__main__': diff --git a/scripts/validate-neo4j-dump.sh b/scripts/validate-neo4j-dump.sh index b6a9c85..e38a1c8 100755 --- a/scripts/validate-neo4j-dump.sh +++ b/scripts/validate-neo4j-dump.sh @@ -8,9 +8,7 @@ tmpfile=$(mktemp /tmp/converted-neo4j-dump-XXX.lp) # Convert from JSON to ASP encoding. -./scripts/translate.py -i "$1" > "$tmpfile" - -cat "$tmpfile" +./scripts/graph-encoder.py -i "$1" > "$tmpfile" ./validate.sh "$tmpfile" "$2" diff --git a/src/grammar.ebnf b/src/grammar.ebnf index 95c475e..6db2b70 100644 --- a/src/grammar.ebnf +++ b/src/grammar.ebnf @@ -4,49 +4,54 @@ edgeshape : "EDGE" shape "[" edgetarget "]" "{" edgeconstraint "}" ";" nodetarget : ":" label | NUMBER -> nid - | "BOTTOM" -> bot - | property -> property + | "BOTTOM" -> bot + | property -> property ?nodeconstraint : nodeconstraint_or ?nodeconstraint_or : [nodeconstraint_or "|"] nodeconstraint_and ?nodeconstraint_and : [nodeconstraint_and "&"] nodeconstraint_basic nodeconstraint_basic : "TOP" -> top | "BOTTOM" -> bot - | shape + | shape -> shaperef | ":" label - | NUMBER -> rnid - | "!" nodeconstraint -> negate - | comp NUMBER path "." nodeconstraint -> greatereq - | comp NUMBER property "." predicate -> countprop - | comp NUMBER edgeconstraint -> greatereqe - | path "==" path -> compare - | path property "==" path property -> comparevalue - | property "==" property -> equals + | NUMBER -> rnid + | "!" nodeconstraint_basic -> negate + | comp NUMBER choicepath "." nodeconstraint_basic -> greatereq + | comp NUMBER property "." predicate -> countprop + | comp NUMBER edgeconstraint -> greatereqe + | choicepath "==" choicepath -> compare + | choicepath property "==" path property -> comparevalue + | property "==" property -> equals | "(" nodeconstraint ")" -path : ":" labelref +?choicepath : [choicepath "|"] concatpath +?concatpath : [concatpath "/"] path +path : ":" label + | "-" path -> negatepath + | "(" choicepath ")" + | path "+" -> repeatpath predicate : "string" -> string | "int" -> int edgetarget : ":" label | NUMBER -> eid - | "BOTTOM" -> bot - | property -> property + | "BOTTOM" -> bot + | property -> property ?edgeconstraint : edgeconstraint_or ?edgeconstraint_or : [edgeconstraint_or "|"] edgeconstraint_and ?edgeconstraint_and : [edgeconstraint_and "&"] edgeconstraint_basic edgeconstraint_basic : "TOP" -> top | "BOTTOM" -> bot - | shape + | shape -> shaperef | ":" label - | NUMBER -> reid - | "!" edgeconstraint -> negate - | comp NUMBER property "." predicate -> countprop - | property "==" property -> equals - | "<<" nodeconstraint -> left - | ">>" nodeconstraint -> right + | NUMBER -> reid + | "!" edgeconstraint_basic -> negate + | comp NUMBER property "." predicate -> countprop + | property "==" property -> equals + | "<<" nodeconstraint_basic -> left + | ">>" nodeconstraint_basic -> right | "(" edgeconstraint ")" comp : "<=" -> le @@ -64,7 +69,7 @@ WORD : LCASE_LETTER CHAR+ CHAR : LETTER | DIGIT | "_" NUMBER : DIGIT+ -COMMENT : "//" /(.)+/ NEWLINE +COMMENT : "%" /(.)*/ NEWLINE %import common.LCASE_LETTER %import common.LETTER diff --git a/src/progs.lp b/src/progs.lp index 2d8eea4..d605039 100644 --- a/src/progs.lp +++ b/src/progs.lp @@ -44,6 +44,16 @@ assignE(E,S,R) :- edgeshape(S,C,_), satisfiesE(E,C,R). assignN(N,S,yes) | assignN(N,S,no) | assignN(N,S,maybe) :- node(N), nodeshape(S). assignE(E,S,yes) | assignE(E,S,no) | assignE(E,S,maybe) :- edge(E), edgeshape(S). +% -- paths -- + +path(N1,N2,label(P)) :- edge(N1,E,N2), label(E,P). +path(N1,N2,concatpath(P1,P2)) :- path(P1), path(P2), path(N1,N,P1), path(N,N2,P2). +path(N1,N2,negatepath(P)) :- path(P), path(N2,N1,P). +path(N1,N2,choicepath(P1,P2)) :- path(P1), path(P2), path(N1,N2,P1). +path(N1,N2,choicepath(P1,P2)) :- path(P1), path(P2), path(N1,N2,P2). +path(N1,N2,repeatpath(P)) :- path(P), path(N1,N2,P). +path(N1,N2,repeatpath(P)) :- path(P), path(N1,N,P), path(N,N2,repeatpath(P)). + % -- top -- satisfiesN(N,top,yes) :- node(N). @@ -58,7 +68,7 @@ satisfiesN(N,shapeRef(S),R) :- node(N), constraint(shapeRef(S)), assignN(N,S,R). satisfiesE(E,shapeRef(S),R) :- edge(E), constraint(shapeRef(S)), - assignN(E,S,R). + assignE(E,S,R). % -- nodeId/edgeID -- @@ -100,25 +110,25 @@ satisfiesE(E,and(C1,C2),R) :- edge(E), constraint(and(C1,C2)), % -- greaterEq -- -satisfiesN(N,greaterEq(L,S,I),yes) :- node(N), constraint(greaterEq(L,S,I)), - countShape(N,L,S,C), C >= I. +satisfiesN(N,greaterEq(P,NC,I),yes) :- node(N), constraint(greaterEq(P,NC,I)), + countNC(N,P,NC,C), C >= I. -satisfiesN(N,greaterEq(L,S,I),no) :- node(N), constraint(greaterEq(L,S,I)), - countAll(N,L,C1), countNoShape(N,L,S,C2), +satisfiesN(N,greaterEq(P,NC,I),no) :- node(N), constraint(greaterEq(P,NC,I)), + countAll(N,P,C1), countNoNC(N,P,NC,C2), C1 - C2 < I. -satisfiesN(N,greaterEq(L,S,I), maybe) :- node(N), constraint(greaterEq(L,S,I)), - not satisfiesN(N,greaterEq(L,S,I),yes), - not satisfiesN(N,greaterEq(L,S,I),no). +satisfiesN(N,greaterEq(P,NC,I), maybe) :- node(N), constraint(greaterEq(P,NC,I)), + not satisfiesN(N,greaterEq(P,NC,I),yes), + not satisfiesN(N,greaterEq(P,NC,I),no). -countAll(N,L,C) :- node(N), labels(L), - #count { Y: edge(N,E,Y), label(E,L) } = C. +countAll(N,P,C) :- node(N), path(P), + #count { Y: path(N,Y,P) } = C. -countShape(N,L,S,C) :- node(N), labels(L), nodeshape(S), - #count { Y: edge(N,E,Y), label(E,L), assignN(Y,S,yes) } = C. +countNC(N,P,NC,C) :- node(N), path(P), constraint(NC), + #count { Y: path(N,Y,P), satisfiesN(Y,NC,yes) } = C. -countNoShape(N,L,S,C) :- node(N), labels(L), nodeshape(S), - #count { Y: edge(N,E,Y), label(E,L), assignN(Y,S,no) } = C. +countNoNC(N,P,NC,C) :- node(N), path(P), constraint(NC), + #count { Y: path(N,Y,P), satisfiesN(Y,NC,no) } = C. % -- compare -- TODO @@ -189,10 +199,5 @@ satisfiesE(E,equals(K1,K2),no) :- edge(E), constraint(equals(K1,K2)), % -- left/right -- -satisfiesE(E,left(NC),yes) :- edge(E), edge(N,E,_), constraint(left(NC)), satisfiesN(N,NC,yes). -satisfiesE(E,left(NC),no) :- edge(E), edge(N,E,_), constraint(left(NC)), satisfiesN(N,NC,no). -satisfiesE(E,left(NC),maybe) :- edge(E), edge(N,E,_), constraint(left(NC)), satisfiesN(N,NC,maybe). - -satisfiesE(E,right(NC),yes) :- edge(E), edge(_,E,N), constraint(right(NC)), satisfiesN(N,NC,yes). -satisfiesE(E,right(NC),no) :- edge(E), edge(_,E,N), constraint(right(NC)), satisfiesN(N,NC,no). -satisfiesE(E,right(NC),maybe) :- edge(E), edge(_,E,N), constraint(right(NC)), satisfiesN(N,NC,maybe). \ No newline at end of file +satisfiesE(E,left(NC),R) :- edge(E), edge(N,E,_), constraint(left(NC)), satisfiesN(N,NC,R). +satisfiesE(E,right(NC),R) :- edge(E), edge(_,E,N), constraint(right(NC)), satisfiesN(N,NC,R). \ No newline at end of file diff --git a/validate.sh b/validate.sh index 12be2b0..030ddcd 100755 --- a/validate.sh +++ b/validate.sh @@ -6,9 +6,7 @@ tmpfile=$(mktemp /tmp/converted-shapes-XXX.lp) -./scripts/shapeTranslator.py -i "$2" > "$tmpfile" - -cat "$tmpfile" +./scripts/shape-transpiler.py -i "$2" > "$tmpfile" clingo src/progs.lp src/display.lp "$1" "$tmpfile"