From 1fb1a2e87cfff780725b4eafabf2ecad97589410 Mon Sep 17 00:00:00 2001 From: Ruslana Tsehanovskaya Date: Fri, 2 Oct 2020 14:51:05 +0300 Subject: [PATCH 1/7] extra task 1 --- src/GraphQueries/GraphQueries.g4 | 73 ++++ src/GraphQueries/MyGraphQueriesVisitor.py | 137 ++++++++ syntax_lang.md | 89 +++++ test/graph_queries_tests.py | 394 ++++++++++++++++++++++ 4 files changed, 693 insertions(+) create mode 100644 src/GraphQueries/GraphQueries.g4 create mode 100644 src/GraphQueries/MyGraphQueriesVisitor.py create mode 100644 syntax_lang.md create mode 100644 test/graph_queries_tests.py diff --git a/src/GraphQueries/GraphQueries.g4 b/src/GraphQueries/GraphQueries.g4 new file mode 100644 index 0000000..52ad206 --- /dev/null +++ b/src/GraphQueries/GraphQueries.g4 @@ -0,0 +1,73 @@ +grammar GraphQueries; + +script : (stmt SEMI)* EOF ; + +stmt : KW_CONNECT KW_TO STRING + | KW_LIST STRING? + | select_stmt + | named_pattern + ; + +named_pattern : NT_NAME OP_EQ pattern ; + +select_stmt : KW_SELECT func KW_FROM STRING KW_WHERE where_expr ; + +func : KW_GET + | KW_COUNT + | KW_EXISTS + ; + +where_expr : LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR ; + +v_expr : INT + | UNDERSCORE + ; + +pattern : elem + | elem MID pattern + ; + +elem : seq + | LBR RBR + ; + +seq : seq_elem + | seq_elem seq + ; + +seq_elem : prim_pattern + | prim_pattern OP_STAR + ; + +prim_pattern : SYMB + | NT_NAME + | LBR pattern RBR + ; + +LBR : '(' ; +RBR : ')' ; +COMMA : ',' ; +SEMI : ';' ; +MID : '|' ; +DOT : '.' ; +UNDERSCORE : '_' ; +OP_STAR : '*' ; +OP_MINUS : '-' ; +OP_GR : '>' ; +OP_EQ : '=' ; +KW_SELECT : 'select' ; +KW_GET : 'get' ; +KW_COUNT : 'count' ; +KW_EXISTS : 'exists' ; +KW_FROM : 'from' ; +KW_WHERE : 'where' ; +KW_LIST : 'list' ; +KW_CONNECT : 'connect' ; +KW_TO : 'to' ; +INT : '0' + | [1-9][0-9]* + ; +SYMB : [a-z]+ ; +NT_NAME : [A-Z]+ ; +STRING : '[' ([a-zA-Z]|[0-9]|('\\' | '-' | '_' | ' ' | '/' | '.' | ',' | ':'))* ']' ; +WS : [ \r\n\t]+ -> skip ; diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py new file mode 100644 index 0000000..4ccd606 --- /dev/null +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +import os +from collections import defaultdict +from antlr4 import * +from algebra import tensor_alg +from chomsky import get_new_nonterm +from cyk import parse_graph +if __name__ is not None and "." in __name__: + from .GraphQueriesParser import GraphQueriesParser +else: + from GraphQueriesParser import GraphQueriesParser + +# This class defines a complete generic visitor for a parse tree produced by GraphQueriesParser. + +class MyGraphQueriesVisitor(ParseTreeVisitor): + def __init__(self, parent=None): + self.addr = "" + self.prods = defaultdict(str) + + + def get_addr(self): + return self.addr + + + def get_prods(self): + return self.prods.items() + + + def select_get(self, start, finish, pattern, graph): + nonterms = self.prods.keys() + add_nonterm = get_new_nonterm("S", nonterms) + _, matrix, _, n = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) + res = [] + if start == "_" and finish == "_": + for i in range(n): + for j in range(n): + if matrix[add_nonterm][i, j]: + res.append((i, j)) + elif start == "_": + for i in range(n): + if matrix[add_nonterm][i, int(finish)]: + res.append((i, int(finish))) + elif finish == "_": + for i in range(n): + if matrix[add_nonterm][int(start), i]: + res.append((int(start), i)) + else: + if matrix[add_nonterm][int(start), int(finish)]: + res.append((int(start), int(finish))) + + return res + + + # Visit a parse tree produced by GraphQueriesParser#script. + def visitScript(self, ctx:GraphQueriesParser.ScriptContext): + self.visitChildren(ctx) + + + # Visit a parse tree produced by GraphQueriesParser#stmt. + def visitStmt(self, ctx:GraphQueriesParser.StmtContext): + if ctx.getChild(0).getText() == "connect": + self.addr = ctx.STRING().getText()[1:-1] + elif ctx.getChild(0).getText() == "list": + if ctx.getChildCount() == 2: + path = ctx.STRING().getText()[1:-1] + else: + path = self.addr + for file in sorted(os.listdir(path)): + print(open(os.path.join(path, file), "r").read() + "\n") + else: + self.visitChildren(ctx) + + + # Visit a parse tree produced by GraphQueriesParser#named_pattern. + def visitNamed_pattern(self, ctx:GraphQueriesParser.Named_patternContext): + nonterm = ctx.NT_NAME().getText() + if self.prods[nonterm]: + self.prods[nonterm] += " | " + self.visitPattern(ctx.pattern()) + else: + self.prods[nonterm] = self.visitPattern(ctx.pattern()) + + + # Visit a parse tree produced by GraphQueriesParser#select_stmt. + def visitSelect_stmt(self, ctx:GraphQueriesParser.Select_stmtContext): + pattern = self.visitPattern(ctx.where_expr().pattern()) + res = self.select_get(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ + parse_graph(ctx.STRING().getText()[1:-1])) + if ctx.func().getText() == "exists": + if res: + print("exists") + else: + print("does not exist") + elif ctx.func().getText() == "count": + print(len(res)) + else: + for u, v in res: + print(str(u) + " " + str(v)) + + + # Visit a parse tree produced by GraphQueriesParser#pattern. + def visitPattern(self, ctx:GraphQueriesParser.PatternContext): + if ctx.getChildCount() == 3: + return self.visitElem(ctx.elem()) + " " + ctx.MID().getText() + " " + self.visitPattern(ctx.pattern()) + return self.visitElem(ctx.elem()) + + + # Visit a parse tree produced by GraphQueriesParser#elem. + def visitElem(self, ctx:GraphQueriesParser.ElemContext): + if ctx.getChildCount() == 1: + return self.visitSeq(ctx.seq()) + else: + return "eps" + + + # Visit a parse tree produced by GraphQueriesParser#seq. + def visitSeq(self, ctx:GraphQueriesParser.SeqContext): + if ctx.getChildCount() == 2: + return self.visitSeq_elem(ctx.seq_elem()) + " " + self.visitSeq(ctx.seq()) + else: + return self.visitSeq_elem(ctx.seq_elem()) + + + # Visit a parse tree produced by GraphQueriesParser#seq_elem. + def visitSeq_elem(self, ctx:GraphQueriesParser.Seq_elemContext): + if ctx.getChildCount() == 2: + return self.visitPrim_pattern(ctx.prim_pattern()) + ctx.getChild(1).getText() + return self.visitPrim_pattern(ctx.prim_pattern()) + + + # Visit a parse tree produced by GraphQueriesParser#prim_pattern. + def visitPrim_pattern(self, ctx:GraphQueriesParser.Prim_patternContext): + if ctx.getChildCount() == 3: + return ctx.LBR().getText() + self.visitPattern(ctx.pattern()) + ctx.RBR().getText() + else: return ctx.getChild(0).getText() + + +del GraphQueriesParser diff --git a/syntax_lang.md b/syntax_lang.md new file mode 100644 index 0000000..2504a24 --- /dev/null +++ b/syntax_lang.md @@ -0,0 +1,89 @@ +### syntax: +``` +script: EPS | stmt SEMI script +stmt: KW_CONNECT KW_TO STRING | lst | select_stmt | named_pattern +lst: KW_LIST | KW_LIST STRING +named_pattern: NT_NAME OP_EQ pattern +select_stmt: KW_SELECT func KW_FROM STRING KW_WHERE where_expr +func: KW_GET | KW_COUNT | KW_EXISTS +where_expr: LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR +v_expr: INT | UNDERSCORE +pattern: elem | elem MID pattern +elem: seq | LBR RBR +seq: seq_elem | seq_elem seq +seq_elem: prim_pattern | prim_pattern OP_STAR +prim_pattern: SYMB | NT_NAME | LBR pattern RBR +``` +### tokens (terminals): +``` +LBR = "(" +RBR = ")" +COMMA = "," +SEMI = ";" +MID = "|" +DOT = "." +UNDERSCORE = "_" +EPS = "" +OP_STAR = "*" +OP_MINUS = "-" +OP_GR = ">" +OP_EQ = "=" +KW_SELECT = "select" +KW_GET = "get" +KW_COUNT = "count" +KW_EXISTS = "exists" +KW_FROM = "from" +KW_WHERE = "where" +KW_LIST = "list" +KW_CONNECT = "connect" +KW_TO = "to" +SYMB = [a − z][a − z]* +INT = 0 | [1 − 9][0 − 9]* +NT_NAME = [A − Z]+ +STRING = "["([aA − zZ] | [0 − 9] | ("\\" | "-"| " " | "_" | "/" | "." | "," | ":"))*"]" +``` +### examples: + +#### patterns +``` +a S (a | b)* +() | (a S)* +``` +#### named patterns +``` +S = a S b | () +A = (a)* +``` +#### connect +``` +connect to [\home\user\graph_db] +``` +#### list + +######by default displays graphs from the connected database if no path is specified +``` +list +list [\home\user\another_graph_db] +``` +#### select statements + +######it is possible to specify the vertex number or write underscore instead (vertex with any number) +###### getting all pairs of vertices that match the conditions: +``` +select get from [graph1.txt] where (_) - (a | b)* -> (_) +``` +###### finding the number of pairs of vertices that match the conditions: +``` +select count from [graph1.txt] where (2) - (a | b)* -> (_) +``` +###### checking for the existence of a pair of vertices that match the conditions: +``` +select exists from [graph1.txt] where (1) - S -> (3) +``` +#### script +``` +connect to [\home\user\graph_db]; +S = a S b S | (); +select count from [graph.txt] where (_) - S -> (1); +``` + diff --git a/test/graph_queries_tests.py b/test/graph_queries_tests.py new file mode 100644 index 0000000..9bc63f4 --- /dev/null +++ b/test/graph_queries_tests.py @@ -0,0 +1,394 @@ +#!/usr/bin/env python3 +import pytest +import os +from antlr_parser import get_stream +from antlr_parser import parse +from MyGraphQueriesVisitor import MyGraphQueriesVisitor + + +def process(stream): + tree = parse(stream) + visitor = MyGraphQueriesVisitor() + visitor.visit(tree) + + return visitor + + +def test_connect1(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("connect to [home/graph_db];") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + + assert visitor.get_addr() == "home/graph_db" + + +def test_connect2(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("connect to [home/graph_db];\nconnect to [home/another_graph_db];") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + + assert visitor.get_addr() == "home/another_graph_db" + + +def test_list1(tmp_path, capsys): + top_dir = tmp_path / "top_dir" + top_dir.mkdir() + file1 = top_dir / "graph1.txt" + file2 = top_dir / "graph2.txt" + file1.write_text("0 a 1\n1 b 2") + file2.write_text("0 a 1\n1 a 0") + file_in = tmp_path / "file.txt" + file_in.write_text("connect to [" + os.path.normpath(top_dir) + "];\nlist;") + process(get_stream(True, os.path.normpath(file_in))) + out, err = capsys.readouterr() + + assert out == "0 a 1\n1 b 2\n\n0 a 1\n1 a 0\n\n" + assert err == "" + + +def test_list2(tmp_path, capsys): + dir1 = tmp_path / "dir1" + dir1.mkdir() + dir2 = tmp_path / "dir2" + dir2.mkdir() + file1 = dir1 / "graph1.txt" + file2 = dir2 / "graph2.txt" + file1.write_text("0 a 1\n1 b 2") + file2.write_text("0 a 1\n1 a 0") + file_in = tmp_path / "file.txt" + file_in.write_text("connect to [" + os.path.normpath(dir1) + "];\nlist [" + os.path.normpath(dir2) + "];") + process(get_stream(True, os.path.normpath(file_in))) + out, err = capsys.readouterr() + + assert out == "0 a 1\n1 a 0\n\n" + assert err == "" + + +def test_named_pattern1(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("S = a S b S;") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + prods = visitor.get_prods() + + assert len(prods) == 1 + assert ("S", "a S b S") in prods + + +def test_named_pattern2(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("S = a S b S;\nA = a;") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + prods = visitor.get_prods() + + assert len(prods) == 2 + assert ("S", "a S b S") in prods + assert ("A", "a") in prods + + +def test_named_pattern3(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("S = a S b S;\nconnect to [data_base];\nS = A B;") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + prods = visitor.get_prods() + + assert len(prods) == 1 + assert ("S", "a S b S | A B") in prods + + +def test_named_pattern4(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("S = a b c;\nS = (c S c)*;") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + prods = visitor.get_prods() + + assert len(prods) == 1 + assert ("S", "a b c | (c S c)*") in prods + + +def test_named_pattern5(tmp_path): + tmp_file = tmp_path / "file.txt" + tmp_file.write_text("A = (a B b)* | c d c (A b)* | (a | c)*;") + visitor = process(get_stream(True, os.path.normpath(tmp_file))) + prods = visitor.get_prods() + + assert len(prods) == 1 + assert ("A", "(a B b)* | c d c (A b)* | (a | c)*") in prods + + + +def test_select_exists1(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 0") + file_script.write_text("S = a S b S;\nS = ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists2(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 0") + file_script.write_text("S = a S b S;\nS = ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists3(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 a 0") + file_script.write_text("S = a S b S;\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "does not exist\n" + assert err == "" + + +def test_select_exists4(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select exists from [" + os.path.normpath(file_graph) + "] where (_) - a b c -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists5(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select exists from [" + os.path.normpath(file_graph) + "] where (_) - a b b -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "does not exist\n" + assert err == "" + + +def test_select_exists6(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 b 1\n1 a 2\n2 b 1") + file_script.write_text("A = a;\nB = b;\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - A B -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists7(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 a 0") + file_script.write_text("S = a S b S;\nS = ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists8(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = a | ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (0) - a b S -> (3);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_exists9(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = a | ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (0) - a b b S -> (3);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "does not exist\n" + assert err == "" + + +def test_select_exists10(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = a | ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (0) - a S S S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_count1(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3") + file_script.write_text("S = a (b | c);\nselect count from [" + os.path.normpath(file_graph) + "] where (0) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "2\n" + assert err == "" + + +def test_select_count2(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("S = a | b | eps;\nselect count from [" + os.path.normpath(file_graph) + "] where (0) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "2\n" + assert err == "" + + +def test_select_count3(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select count from [" + os.path.normpath(file_graph) + "] where (0) - (a | b)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "4\n" + assert err == "" + + +def test_select_count4(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select count from [" + os.path.normpath(file_graph) + "] where (_) - a b -> (0);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0\n" + assert err == "" + + +def test_select_count5(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select count from [" + os.path.normpath(file_graph) + "] where (_) - a b -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "1\n" + assert err == "" + + +def test_select_count6(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select count from [" + os.path.normpath(file_graph) + "] where (1) - (a | b)* | c -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "4\n" + assert err == "" + + +def test_select_count7(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = a | ();\nselect count from [" + os.path.normpath(file_graph) + "] where (_) - S S S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "6\n" + assert err == "" + + +def test_select_get1(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3") + file_script.write_text("S = a (b | c);\nselect get from [" + os.path.normpath(file_graph) + "] where (0) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 2\n0 3\n" + assert err == "" + + +def test_select_get2(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select get from [" + os.path.normpath(file_graph) + "] where (0) - (a | b)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n0 2\n0 4\n" + assert err == "" + + +def test_select_get3(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select get from [" + os.path.normpath(file_graph) + "] where (_) - a b -> (0);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "" + assert err == "" + + +def test_select_get4(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select get from [" + os.path.normpath(file_graph) + "] where (_) - (a | b)* | c -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n0 2\n0 4\n1 1\n1 2\n1 3\n1 4\n2 2\n2 4\n3 3\n4 4\n" + assert err == "" + + +def test_select_get5(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = A B;\nA = a | ();\nB = b | ();\nselect get from [" + os.path.normpath(file_graph) + "]\ + where (_) - S -> (2);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 2\n1 2\n2 2\n" + assert err == "" + + +def test_script(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n1 c 3\n2 a 4") + file_script.write_text("select count from [" + os.path.normpath(file_graph) + "] where (0) - (a | b)* -> (_);\n\ + S = (a | b)*;\nselect get from [" + os.path.normpath(file_graph) + "] where (0) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "4\n0 0\n0 1\n0 2\n0 4\n" + assert err == "" From b4842563ad4aae7599a4ede3bbabe4d457312521 Mon Sep 17 00:00:00 2001 From: Ruslana Tsehanovskaya Date: Fri, 2 Oct 2020 15:11:30 +0300 Subject: [PATCH 2/7] extra task 2 --- src/GraphQueries/GraphQueries.g4 | 3 +- src/GraphQueries/MyGraphQueriesVisitor.py | 18 ++++++++---- syntax_lang.md | 12 +++++--- test/graph_queries_tests.py | 34 +++++++++++++++++++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/GraphQueries/GraphQueries.g4 b/src/GraphQueries/GraphQueries.g4 index 52ad206..87fd325 100644 --- a/src/GraphQueries/GraphQueries.g4 +++ b/src/GraphQueries/GraphQueries.g4 @@ -3,7 +3,7 @@ grammar GraphQueries; script : (stmt SEMI)* EOF ; stmt : KW_CONNECT KW_TO STRING - | KW_LIST STRING? + | KW_LIST KW_ALL? STRING? | select_stmt | named_pattern ; @@ -62,6 +62,7 @@ KW_EXISTS : 'exists' ; KW_FROM : 'from' ; KW_WHERE : 'where' ; KW_LIST : 'list' ; +KW_ALL : 'all' ; KW_CONNECT : 'connect' ; KW_TO : 'to' ; INT : '0' diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py index 4ccd606..2fc4904 100644 --- a/src/GraphQueries/MyGraphQueriesVisitor.py +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -61,12 +61,20 @@ def visitStmt(self, ctx:GraphQueriesParser.StmtContext): if ctx.getChild(0).getText() == "connect": self.addr = ctx.STRING().getText()[1:-1] elif ctx.getChild(0).getText() == "list": - if ctx.getChildCount() == 2: - path = ctx.STRING().getText()[1:-1] + if ctx.getChild(1).getText() == "all": + if ctx.getChildCount() == 3: + path = ctx.STRING().getText()[1:-1] + else: + path = self.addr + for file in sorted(os.listdir(path)): + print(open(os.path.join(path, file), "r").read() + "\n") else: - path = self.addr - for file in sorted(os.listdir(path)): - print(open(os.path.join(path, file), "r").read() + "\n") + filename = ctx.STRING().getText()[1:-1] + labels = set() + with open(os.path.join(filename)) as file: + for line in file.readlines(): + labels.add(line.split()[1]) + print(" ".join(sorted(list(labels)))) else: self.visitChildren(ctx) diff --git a/syntax_lang.md b/syntax_lang.md index 2504a24..af72a53 100644 --- a/syntax_lang.md +++ b/syntax_lang.md @@ -1,8 +1,7 @@ ### syntax: ``` script: EPS | stmt SEMI script -stmt: KW_CONNECT KW_TO STRING | lst | select_stmt | named_pattern -lst: KW_LIST | KW_LIST STRING +stmt: KW_CONNECT KW_TO STRING | KW_LIST KW_ALL? STRING? | select_stmt | named_pattern named_pattern: NT_NAME OP_EQ pattern select_stmt: KW_SELECT func KW_FROM STRING KW_WHERE where_expr func: KW_GET | KW_COUNT | KW_EXISTS @@ -35,6 +34,7 @@ KW_EXISTS = "exists" KW_FROM = "from" KW_WHERE = "where" KW_LIST = "list" +KW_ALL : 'all' ; KW_CONNECT = "connect" KW_TO = "to" SYMB = [a − z][a − z]* @@ -62,8 +62,12 @@ connect to [\home\user\graph_db] ######by default displays graphs from the connected database if no path is specified ``` -list -list [\home\user\another_graph_db] +list all +list all [\home\user\another_graph_db] +``` +######print set of different edge labels in the specified graph +``` +list [\home\user\agraph_db\graph1.txt] ``` #### select statements diff --git a/test/graph_queries_tests.py b/test/graph_queries_tests.py index 9bc63f4..eb767f4 100644 --- a/test/graph_queries_tests.py +++ b/test/graph_queries_tests.py @@ -38,7 +38,7 @@ def test_list1(tmp_path, capsys): file1.write_text("0 a 1\n1 b 2") file2.write_text("0 a 1\n1 a 0") file_in = tmp_path / "file.txt" - file_in.write_text("connect to [" + os.path.normpath(top_dir) + "];\nlist;") + file_in.write_text("connect to [" + os.path.normpath(top_dir) + "];\nlist all;") process(get_stream(True, os.path.normpath(file_in))) out, err = capsys.readouterr() @@ -56,7 +56,7 @@ def test_list2(tmp_path, capsys): file1.write_text("0 a 1\n1 b 2") file2.write_text("0 a 1\n1 a 0") file_in = tmp_path / "file.txt" - file_in.write_text("connect to [" + os.path.normpath(dir1) + "];\nlist [" + os.path.normpath(dir2) + "];") + file_in.write_text("connect to [" + os.path.normpath(dir1) + "];\nlist all [" + os.path.normpath(dir2) + "];") process(get_stream(True, os.path.normpath(file_in))) out, err = capsys.readouterr() @@ -64,6 +64,36 @@ def test_list2(tmp_path, capsys): assert err == "" +def test_list3(tmp_path, capsys): + dir1 = tmp_path / "dir1" + dir1.mkdir() + file1 = dir1 / "graph1.txt" + file2 = dir1 / "graph2.txt" + file1.write_text("0 a 1\n1 b 2\n2 c 3") + file2.write_text("0 a 1\n1 a 0\n1 b 2") + file_in = tmp_path / "file.txt" + file_in.write_text("connect to [" + os.path.normpath(dir1) + "];\nlist [" + os.path.normpath(file2) + "];") + process(get_stream(True, os.path.normpath(file_in))) + out, err = capsys.readouterr() + + assert out == "a b\n" + assert err == "" + + +def test_list4(tmp_path, capsys): + file1 = tmp_path / "graph1.txt" + file2 = tmp_path / "graph2.txt" + file1.write_text("0 a 1\n1 b 2\n2 c 3") + file2.write_text("0 d 1\n1 d 0\n1 d 2\n2 c 1") + file_in = tmp_path / "file.txt" + file_in.write_text("list [" + os.path.normpath(file2) + "];") + process(get_stream(True, os.path.normpath(file_in))) + out, err = capsys.readouterr() + + assert out == "c d\n" + assert err == "" + + def test_named_pattern1(tmp_path): tmp_file = tmp_path / "file.txt" tmp_file.write_text("S = a S b S;") From 16cca64042122a4dc4e33c152637549cb9e82941 Mon Sep 17 00:00:00 2001 From: Ruslana Tsehanovskaya Date: Fri, 2 Oct 2020 16:07:59 +0300 Subject: [PATCH 3/7] extra task 3 --- src/GraphQueries/GraphQueries.g4 | 11 ++++- src/GraphQueries/MyGraphQueriesVisitor.py | 26 +++++++++++- syntax_lang.md | 19 ++++++--- test/graph_queries_tests.py | 49 +++++++++++++++++++++++ 4 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/GraphQueries/GraphQueries.g4 b/src/GraphQueries/GraphQueries.g4 index 87fd325..e8a9a0a 100644 --- a/src/GraphQueries/GraphQueries.g4 +++ b/src/GraphQueries/GraphQueries.g4 @@ -10,13 +10,18 @@ stmt : KW_CONNECT KW_TO STRING named_pattern : NT_NAME OP_EQ pattern ; -select_stmt : KW_SELECT func KW_FROM STRING KW_WHERE where_expr ; +select_stmt : KW_SELECT func KW_FROM STRING KW_WHERE where_expr alg? ; func : KW_GET | KW_COUNT | KW_EXISTS ; +alg : KW_USING KW_HELLINGS + | KW_USING KW_MATRICES + | KW_USING KW_TENSORS + ; + where_expr : LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR ; v_expr : INT @@ -65,6 +70,10 @@ KW_LIST : 'list' ; KW_ALL : 'all' ; KW_CONNECT : 'connect' ; KW_TO : 'to' ; +KW_USING : 'using' ; +KW_HELLINGS : 'hellings' ; +KW_MATRICES : 'matrices' ; +KW_TENSORS : 'tensors' ; INT : '0' | [1-9][0-9]* ; diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py index 2fc4904..62812e1 100644 --- a/src/GraphQueries/MyGraphQueriesVisitor.py +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -2,9 +2,12 @@ import os from collections import defaultdict from antlr4 import * +from chomsky import to_weak_CNF +from algebra import matrix_alg from algebra import tensor_alg from chomsky import get_new_nonterm from cyk import parse_graph +from cyk import Hellings if __name__ is not None and "." in __name__: from .GraphQueriesParser import GraphQueriesParser else: @@ -26,7 +29,7 @@ def get_prods(self): return self.prods.items() - def select_get(self, start, finish, pattern, graph): + def select_get_tensors(self, start, finish, pattern, graph): nonterms = self.prods.keys() add_nonterm = get_new_nonterm("S", nonterms) _, matrix, _, n = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) @@ -51,6 +54,21 @@ def select_get(self, start, finish, pattern, graph): return res + def select_get(self, start, finish, pattern, graph, alg): + nonterms = self.prods.keys() + add_nonterm = get_new_nonterm("S", nonterms) + new_prods = [] + for nonterm in self.prods.keys(): + lst = self.prods[nonterm].split(" | ") + new_prods += [(nonterm, element.split()) for element in lst] + new_prods = to_weak_CNF(new_prods + [(add_nonterm, pattern.split())], add_nonterm) + if alg == "hellings": + res = Hellings(new_prods, graph) + else: + res = matrix_alg(new_prods, graph) + return [(u, v) for (nonterm, u, v) in res if nonterm == add_nonterm] + + # Visit a parse tree produced by GraphQueriesParser#script. def visitScript(self, ctx:GraphQueriesParser.ScriptContext): self.visitChildren(ctx) @@ -91,8 +109,12 @@ def visitNamed_pattern(self, ctx:GraphQueriesParser.Named_patternContext): # Visit a parse tree produced by GraphQueriesParser#select_stmt. def visitSelect_stmt(self, ctx:GraphQueriesParser.Select_stmtContext): pattern = self.visitPattern(ctx.where_expr().pattern()) - res = self.select_get(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ + if ctx.getChildCount() == 6 or (ctx.getChildCount() == 7 and ctx.alg().getChild(1).getText() == "tensors"): + res = self.select_get_tensors(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ parse_graph(ctx.STRING().getText()[1:-1])) + else: + res = self.select_get(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ + parse_graph(ctx.STRING().getText()[1:-1]), ctx.alg().getChild(1).getText()) if ctx.func().getText() == "exists": if res: print("exists") diff --git a/syntax_lang.md b/syntax_lang.md index af72a53..7dd330c 100644 --- a/syntax_lang.md +++ b/syntax_lang.md @@ -3,8 +3,9 @@ script: EPS | stmt SEMI script stmt: KW_CONNECT KW_TO STRING | KW_LIST KW_ALL? STRING? | select_stmt | named_pattern named_pattern: NT_NAME OP_EQ pattern -select_stmt: KW_SELECT func KW_FROM STRING KW_WHERE where_expr +select_stmt: KW_SELECT func KW_FROM STRING KW_WHERE where_expr alg? func: KW_GET | KW_COUNT | KW_EXISTS +alg: KW_USING KW_HELLINGS | KW_USING KW_MATRICES | KW_USING KW_TENSORS where_expr: LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR v_expr: INT | UNDERSCORE pattern: elem | elem MID pattern @@ -34,9 +35,13 @@ KW_EXISTS = "exists" KW_FROM = "from" KW_WHERE = "where" KW_LIST = "list" -KW_ALL : 'all' ; +KW_ALL : "all" ; KW_CONNECT = "connect" KW_TO = "to" +KW_USING = 'using' +KW_HELLINGS = "hellings" +KW_MATRICES = "matrices" +KW_TENSORS = "tensors" SYMB = [a − z][a − z]* INT = 0 | [1 − 9][0 − 9]* NT_NAME = [A − Z]+ @@ -60,18 +65,18 @@ connect to [\home\user\graph_db] ``` #### list -######by default displays graphs from the connected database if no path is specified +###### by default displays graphs from the connected database if no path is specified ``` list all list all [\home\user\another_graph_db] ``` -######print set of different edge labels in the specified graph +###### print set of different edge labels in the specified graph ``` list [\home\user\agraph_db\graph1.txt] ``` #### select statements -######it is possible to specify the vertex number or write underscore instead (vertex with any number) +###### it is possible to specify the vertex number or write underscore instead (vertex with any number) ###### getting all pairs of vertices that match the conditions: ``` select get from [graph1.txt] where (_) - (a | b)* -> (_) @@ -84,6 +89,10 @@ select count from [graph1.txt] where (2) - (a | b)* -> (_) ``` select exists from [graph1.txt] where (1) - S -> (3) ``` +###### it is possible to specify the used algorithm: "using hellings", "using matrices" or "using tensors" (by default the algorithm with tensors is used): +``` +select count from [graph1.txt] where (2) - (a | b)* -> (_) using hellings +``` #### script ``` connect to [\home\user\graph_db]; diff --git a/test/graph_queries_tests.py b/test/graph_queries_tests.py index eb767f4..f8d09e7 100644 --- a/test/graph_queries_tests.py +++ b/test/graph_queries_tests.py @@ -422,3 +422,52 @@ def test_script(tmp_path, capsys): assert out == "4\n0 0\n0 1\n0 2\n0 4\n" assert err == "" + + +def test_select_using_hellings1(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 a 0") + file_script.write_text("S = a S b S;\nS = ();\nselect exists from [" + os.path.normpath(file_graph) + "] where (_) - S -> (_) using hellings;") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "exists\n" + assert err == "" + + +def test_select_using_hellings2(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select exists from [" + os.path.normpath(file_graph) + "] where (_) - a b b -> (_) using hellings;") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "does not exist\n" + assert err == "" + + +def test_select_using_matrices(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = a;\nS = ();\nselect count from [" + os.path.normpath(file_graph) + "] where (_) - S S S -> (_) using matrices;") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "6\n" + assert err == "" + + +def test_select_using_tensors(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph = tmp_path / "graph.txt" + file_graph.write_text("0 a 1\n1 b 2\n2 a 3") + file_script.write_text("S = A B;\nA = a | ();\nB = b | ();\nselect get from [" + os.path.normpath(file_graph) + "]\ + where (_) - S -> (2) using tensors;") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 2\n1 2\n2 2\n" + assert err == "" From 10ef1a799e7fba03220b3cb8f6777202319cf748 Mon Sep 17 00:00:00 2001 From: lanasheep <41874174+lanasheep@users.noreply.github.com> Date: Fri, 2 Oct 2020 19:51:05 +0300 Subject: [PATCH 4/7] fix bug --- src/GraphQueries/MyGraphQueriesVisitor.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py index 62812e1..4ee6d21 100644 --- a/src/GraphQueries/MyGraphQueriesVisitor.py +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -66,7 +66,25 @@ def select_get(self, start, finish, pattern, graph, alg): res = Hellings(new_prods, graph) else: res = matrix_alg(new_prods, graph) - return [(u, v) for (nonterm, u, v) in res if nonterm == add_nonterm] + ans = [] + if start == "_" and finish == "_": + for nonterm, u, v in res: + if nonterm == add_nonterm: + ans.append((u, v)) + elif start == "_": + for nonterm, u, v in res: + if nonterm == add_nonterm and v == int(finish): + ans.append((u, v)) + elif finish == "_": + for nonterm, u, v in res: + if nonterm == add_nonterm and u == int(start): + ans.append((u, v)) + else: + for nonterm, u, v in res: + if nonterm == add_nonterm and u == int(start) and v == int(finish): + ans.append((u, v)) + + return ans # Visit a parse tree produced by GraphQueriesParser#script. From 5d9c7546343d1b1ec9dceeb3793e6c990e4a1e00 Mon Sep 17 00:00:00 2001 From: Ruslana Tsehanovskaya Date: Fri, 2 Oct 2020 23:41:37 +0300 Subject: [PATCH 5/7] extra task 4 --- src/GraphQueries/GraphQueries.g4 | 14 +++++- src/GraphQueries/MyGraphQueriesVisitor.py | 59 +++++++++++++++++------ src/graph_lang.py | 34 +++++++++++++ syntax_lang.md | 12 ++++- 4 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 src/graph_lang.py diff --git a/src/GraphQueries/GraphQueries.g4 b/src/GraphQueries/GraphQueries.g4 index e8a9a0a..e92ddfa 100644 --- a/src/GraphQueries/GraphQueries.g4 +++ b/src/GraphQueries/GraphQueries.g4 @@ -10,7 +10,7 @@ stmt : KW_CONNECT KW_TO STRING named_pattern : NT_NAME OP_EQ pattern ; -select_stmt : KW_SELECT func KW_FROM STRING KW_WHERE where_expr alg? ; +select_stmt : KW_SELECT func KW_FROM from_expr KW_WHERE where_expr alg? ; func : KW_GET | KW_COUNT @@ -22,12 +22,21 @@ alg : KW_USING KW_HELLINGS | KW_USING KW_TENSORS ; +from_expr : graph_expr? STRING + ; + where_expr : LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR ; v_expr : INT | UNDERSCORE ; +graph_expr : STRING + | KW_INTERSEC LBR graph_expr COMMA graph_expr RBR + | KW_UNION LBR graph_expr COMMA graph_expr RBR + | KW_COMPL LBR graph_expr RBR + ; + pattern : elem | elem MID pattern ; @@ -74,6 +83,9 @@ KW_USING : 'using' ; KW_HELLINGS : 'hellings' ; KW_MATRICES : 'matrices' ; KW_TENSORS : 'tensors' ; +KW_INTERSEC : 'intersec' ; +KW_UNION : 'union' ; +KW_COMPL : 'compl' ; INT : '0' | [1-9][0-9]* ; diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py index 4ee6d21..75f4bf6 100644 --- a/src/GraphQueries/MyGraphQueriesVisitor.py +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -6,8 +6,8 @@ from algebra import matrix_alg from algebra import tensor_alg from chomsky import get_new_nonterm -from cyk import parse_graph from cyk import Hellings +from graph_lang import * if __name__ is not None and "." in __name__: from .GraphQueriesParser import GraphQueriesParser else: @@ -29,31 +29,35 @@ def get_prods(self): return self.prods.items() - def select_get_tensors(self, start, finish, pattern, graph): - nonterms = self.prods.keys() - add_nonterm = get_new_nonterm("S", nonterms) - _, matrix, _, n = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) + def get_ans(self, start, finish, nonterm, matrix, n): res = [] if start == "_" and finish == "_": for i in range(n): for j in range(n): - if matrix[add_nonterm][i, j]: + if matrix[nonterm][i, j]: res.append((i, j)) elif start == "_": for i in range(n): - if matrix[add_nonterm][i, int(finish)]: + if matrix[nonterm][i, int(finish)]: res.append((i, int(finish))) elif finish == "_": for i in range(n): - if matrix[add_nonterm][int(start), i]: + if matrix[nonterm][int(start), i]: res.append((int(start), i)) else: - if matrix[add_nonterm][int(start), int(finish)]: + if matrix[nonterm][int(start), int(finish)]: res.append((int(start), int(finish))) return res + def select_get_tensors(self, start, finish, pattern, graph): + nonterms = self.prods.keys() + add_nonterm = get_new_nonterm("S", nonterms) + _, matrix, _, n = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) + return self.get_ans(start, finish, add_nonterm, matrix, n) + + def select_get(self, start, finish, pattern, graph, alg): nonterms = self.prods.keys() add_nonterm = get_new_nonterm("S", nonterms) @@ -87,6 +91,13 @@ def select_get(self, start, finish, pattern, graph, alg): return ans + def select_get_graph_lang(self, start, finish, pattern, graph, automata): + nonterms = self.prods.keys() + add_nonterm = get_new_nonterm("S", nonterms) + _, matrix, _, n = graph_lang_intersect(list(self.prods.items()) + [(add_nonterm, pattern)], automata, graph) + return self.get_ans(start, finish, add_nonterm, matrix, n) + + # Visit a parse tree produced by GraphQueriesParser#script. def visitScript(self, ctx:GraphQueriesParser.ScriptContext): self.visitChildren(ctx) @@ -126,13 +137,20 @@ def visitNamed_pattern(self, ctx:GraphQueriesParser.Named_patternContext): # Visit a parse tree produced by GraphQueriesParser#select_stmt. def visitSelect_stmt(self, ctx:GraphQueriesParser.Select_stmtContext): - pattern = self.visitPattern(ctx.where_expr().pattern()) - if ctx.getChildCount() == 6 or (ctx.getChildCount() == 7 and ctx.alg().getChild(1).getText() == "tensors"): - res = self.select_get_tensors(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ - parse_graph(ctx.STRING().getText()[1:-1])) + if ctx.from_expr().getChildCount() == 1: + pattern = self.visitPattern(ctx.where_expr().pattern()) + if ctx.getChildCount() == 6 or (ctx.getChildCount() == 7 and ctx.alg().getChild(1).getText() == "tensors"): + res = self.select_get_tensors(ctx.where_expr().getChild(1).getText(), + ctx.where_expr().getChild(8).getText(), pattern, + parse_graph(ctx.from_expr().STRING().getText()[1:-1])) + else: + res = self.select_get(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), + pattern, parse_graph(ctx.from_expr().STRING().getText()[1:-1]), ctx.alg().getChild(1).getText()) else: - res = self.select_get(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), pattern, \ - parse_graph(ctx.STRING().getText()[1:-1]), ctx.alg().getChild(1).getText()) + pattern = self.visitPattern(ctx.where_expr().pattern()) + automata = self.visitGraph_expr(ctx.from_expr().graph_expr()) + res = self.select_get_graph_lang(ctx.where_expr().getChild(1).getText(), ctx.where_expr().getChild(8).getText(), + pattern, parse_graph(ctx.from_expr().STRING().getText()[1:-1]), automata) if ctx.func().getText() == "exists": if res: print("exists") @@ -145,6 +163,17 @@ def visitSelect_stmt(self, ctx:GraphQueriesParser.Select_stmtContext): print(str(u) + " " + str(v)) + # Visit a parse tree produced by GraphQueriesParser#graph_expr. + def visitGraph_expr(self, ctx: GraphQueriesParser.Graph_exprContext): + if ctx.getChildCount() == 1: + return build_automata_from_graph(ctx.STRING()[1:-1]) + elif ctx.getChild(0).getText() == "intersec": + return intersec(self.visitGraph_expr(ctx.getChild(2)), self.visitGraph_expr(ctx.getChild(4))) + elif ctx.getChild(0).getText() == "union": + return union(self.visitGraph_expr(ctx.getChild(2)), self.visitGraph_expr(ctx.getChild(4))) + else: + return compl(self.visitGraph_expr(ctx.getChild(2))) + # Visit a parse tree produced by GraphQueriesParser#pattern. def visitPattern(self, ctx:GraphQueriesParser.PatternContext): if ctx.getChildCount() == 3: diff --git a/src/graph_lang.py b/src/graph_lang.py new file mode 100644 index 0000000..0f08ec3 --- /dev/null +++ b/src/graph_lang.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +from pyformlang.finite_automaton import State +from pyformlang.finite_automaton import Symbol +from pyformlang.finite_automaton import NondeterministicFiniteAutomaton +from cyk import parse_graph + +def build_automata_from_graph(filename): + automata = NondeterministicFiniteAutomaton() + graph = parse_graph(filename) + vert = set() + for u, symb, v in graph: + vert.add(u) + vert.add(v) + for v in vert: + automata.add_start_state(State((filename, v))) + automata.add_final_state(State((filename, v))) + for u, symb, v in graph: + automata.add_transition(State((filename, u)), Symbol(symb), State((filename, v))) + + +def intersec(automata1, automata2): + return automata1.get_intersection(automata2) + + +def union(automata1, automata2): + return compl(intersec(compl(automata1), compl(automata2))) + + +def compl(automata): + return automata.get_complement() + + +def graph_lang_intersect(prods, automata, graph): + pass diff --git a/syntax_lang.md b/syntax_lang.md index 7dd330c..8c2bcaf 100644 --- a/syntax_lang.md +++ b/syntax_lang.md @@ -3,11 +3,14 @@ script: EPS | stmt SEMI script stmt: KW_CONNECT KW_TO STRING | KW_LIST KW_ALL? STRING? | select_stmt | named_pattern named_pattern: NT_NAME OP_EQ pattern -select_stmt: KW_SELECT func KW_FROM STRING KW_WHERE where_expr alg? +select_stmt: KW_SELECT func KW_FROM from_expr KW_WHERE where_expr alg? func: KW_GET | KW_COUNT | KW_EXISTS alg: KW_USING KW_HELLINGS | KW_USING KW_MATRICES | KW_USING KW_TENSORS +from_expr: graph_expr? STRING where_expr: LBR v_expr RBR OP_MINUS pattern OP_MINUS OP_GR LBR v_expr RBR v_expr: INT | UNDERSCORE +graph_expr: STRING | KW_INTERSEC LBR graph_expr COMMA graph_expr RBR | +KW_UNION LBR graph_expr COMMA graph_expr RBR | KW_COMPL LBR graph_expr RBR pattern: elem | elem MID pattern elem: seq | LBR RBR seq: seq_elem | seq_elem seq @@ -42,6 +45,9 @@ KW_USING = 'using' KW_HELLINGS = "hellings" KW_MATRICES = "matrices" KW_TENSORS = "tensors" +KW_INTERSEC = "intersec" +KW_UNION = "union" +KW_COMPL = "compl" SYMB = [a − z][a − z]* INT = 0 | [1 − 9][0 − 9]* NT_NAME = [A − Z]+ @@ -93,6 +99,10 @@ select exists from [graph1.txt] where (1) - S -> (3) ``` select count from [graph1.txt] where (2) - (a | b)* -> (_) using hellings ``` +######adding a graph expression means that the paths between the required pairs of vertices must also satisfy the condition of being in the language specified by this expression +``` +select count from intersec ([graph1.txt], compl (graph2.txt)) graph3.txt where (_) - S -> (3) +``` #### script ``` connect to [\home\user\graph_db]; From 0ffd07b0a690b4a678e1b9db4c05d5accc30f70e Mon Sep 17 00:00:00 2001 From: lanasheep <41874174+lanasheep@users.noreply.github.com> Date: Sat, 3 Oct 2020 20:05:08 +0300 Subject: [PATCH 6/7] fix syntax description --- syntax_lang.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax_lang.md b/syntax_lang.md index 8c2bcaf..294c62c 100644 --- a/syntax_lang.md +++ b/syntax_lang.md @@ -101,7 +101,7 @@ select count from [graph1.txt] where (2) - (a | b)* -> (_) using hellings ``` ######adding a graph expression means that the paths between the required pairs of vertices must also satisfy the condition of being in the language specified by this expression ``` -select count from intersec ([graph1.txt], compl (graph2.txt)) graph3.txt where (_) - S -> (3) +select count from intersec ([graph1.txt], compl ([graph2.txt])) [graph3.txt] where (_) - S -> (3) ``` #### script ``` From 5043189b529d3f206b4bedb92e86623ed467b6f1 Mon Sep 17 00:00:00 2001 From: Ruslana Tsehanovskaya Date: Sun, 4 Oct 2020 17:50:43 +0300 Subject: [PATCH 7/7] extra task 4 completed --- src/GraphQueries/MyGraphQueriesVisitor.py | 16 +- src/algebra.py | 238 ++++++++++++++++++++++ src/graph_lang.py | 23 ++- syntax_lang.md | 3 +- test/graph_queries_tests.py | 199 ++++++++++++++++++ 5 files changed, 467 insertions(+), 12 deletions(-) create mode 100644 src/algebra.py diff --git a/src/GraphQueries/MyGraphQueriesVisitor.py b/src/GraphQueries/MyGraphQueriesVisitor.py index 75f4bf6..530a362 100644 --- a/src/GraphQueries/MyGraphQueriesVisitor.py +++ b/src/GraphQueries/MyGraphQueriesVisitor.py @@ -6,6 +6,7 @@ from algebra import matrix_alg from algebra import tensor_alg from chomsky import get_new_nonterm +from cyk import parse_graph from cyk import Hellings from graph_lang import * if __name__ is not None and "." in __name__: @@ -29,7 +30,7 @@ def get_prods(self): return self.prods.items() - def get_ans(self, start, finish, nonterm, matrix, n): + def get_res(self, start, finish, nonterm, matrix, n): res = [] if start == "_" and finish == "_": for i in range(n): @@ -55,7 +56,7 @@ def select_get_tensors(self, start, finish, pattern, graph): nonterms = self.prods.keys() add_nonterm = get_new_nonterm("S", nonterms) _, matrix, _, n = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) - return self.get_ans(start, finish, add_nonterm, matrix, n) + return self.get_res(start, finish, add_nonterm, matrix, n) def select_get(self, start, finish, pattern, graph, alg): @@ -94,8 +95,12 @@ def select_get(self, start, finish, pattern, graph, alg): def select_get_graph_lang(self, start, finish, pattern, graph, automata): nonterms = self.prods.keys() add_nonterm = get_new_nonterm("S", nonterms) - _, matrix, _, n = graph_lang_intersect(list(self.prods.items()) + [(add_nonterm, pattern)], automata, graph) - return self.get_ans(start, finish, add_nonterm, matrix, n) + _, matrix1, _, n1 = tensor_alg(list(self.prods.items()) + [(add_nonterm, pattern)], graph) + _, matrix2, _, n2 = tensor_alg([(add_nonterm, "")], graph, automata) + res1 = self.get_res(start, finish, add_nonterm, matrix1, n1) + res2 = self.get_res(start, finish, add_nonterm, matrix2, n2) + + return sorted(list(set(res1) & set(res2))) # Visit a parse tree produced by GraphQueriesParser#script. @@ -166,7 +171,7 @@ def visitSelect_stmt(self, ctx:GraphQueriesParser.Select_stmtContext): # Visit a parse tree produced by GraphQueriesParser#graph_expr. def visitGraph_expr(self, ctx: GraphQueriesParser.Graph_exprContext): if ctx.getChildCount() == 1: - return build_automata_from_graph(ctx.STRING()[1:-1]) + return build_automata_from_graph(ctx.STRING().getText()[1:-1]) elif ctx.getChild(0).getText() == "intersec": return intersec(self.visitGraph_expr(ctx.getChild(2)), self.visitGraph_expr(ctx.getChild(4))) elif ctx.getChild(0).getText() == "union": @@ -174,6 +179,7 @@ def visitGraph_expr(self, ctx: GraphQueriesParser.Graph_exprContext): else: return compl(self.visitGraph_expr(ctx.getChild(2))) + # Visit a parse tree produced by GraphQueriesParser#pattern. def visitPattern(self, ctx:GraphQueriesParser.PatternContext): if ctx.getChildCount() == 3: diff --git a/src/algebra.py b/src/algebra.py new file mode 100644 index 0000000..b984286 --- /dev/null +++ b/src/algebra.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +import scipy +from collections import defaultdict +from pyformlang.finite_automaton import NondeterministicFiniteAutomaton +from automata import reg2min_dfa +from chomsky import eps +from chomsky import is_term +from chomsky import parse_grammar +from chomsky import print_grammar +from chomsky import to_weak_CNF +from cyk import parse_graph +from cyk import print_res + + +def parse_ext_grammar(filename): + prods = [] + with open(filename) as file: + for line in file.readlines(): + lst = line.split()[1:] + prods.append((line.split()[0], " ".join(lst))) + + return prods + + +def print_res_tensor(start, matrix_automata, matrix_graph, m, n, filename): + ans = [[[] for _ in range(m + 1)] for _ in range(m + 1)] + + for symb in matrix_automata.keys(): + for i in range(m): + for j in range(m): + if matrix_automata[symb][i, j]: + ans[i][j].append(str(symb.value)) + + with open(filename, "w") as file: + for i in range(m): + for j in range(m): + if ans[i][j]: + ans[i][j].sort() + file.write("[" + ", ".join(ans[i][j]) + "] ") + else: + file.write(". ") + file.write("\n") + + for i in range(n): + for j in range(n): + if matrix_graph[start][i, j]: + file.write(str(i) + " " + str(j) + "\n") + + +def matrix_alg(prods, graph): + dict = defaultdict(list) + nonterms = set() + + for prod in prods: + if (len(prod[1]) == 1) and is_term(prod[1][0]): + dict[prod[1][0]].append(prod[0]) + nonterms.add(prod[0]) + + vert = set() + for v, _, u in graph: + vert.add(v) + vert.add(u) + + n = len(vert) + rows = defaultdict(list) + cols = defaultdict(list) + data = defaultdict(list) + + for v, symb, u in graph: + for x in dict[symb]: + rows[x].append(v) + cols[x].append(u) + data[x].append(True) + + for i in range(n): + for x in dict[eps]: + rows[x].append(i) + cols[x].append(i) + data[x].append(True) + + res = defaultdict() + for nonterm in nonterms: + res[nonterm] = scipy.sparse.csr_matrix((data[nonterm], (rows[nonterm], cols[nonterm])), shape=(n, n), dtype=bool) + + change = True + while change: + change = False + for prod in prods: + if len(prod[1]) == 2 and not is_term(prod[1][0]) and not is_term(prod[1][1]): + a = prod[0] + b = prod[1][0] + c = prod[1][1] + if (res[a] != res[a] + res[b] * res[c]).count_nonzero() > 0: + change = True + res[a] = res[a] + res[b] * res[c] + + ans = [] + for symb in res.keys(): + for i in range(n): + for j in range(n): + if res[symb][i, j]: + ans.append((symb, i, j)) + + return ans + + +def build_automata(prods, automata_intersec=None): + start_set = defaultdict(str) + final_set = defaultdict(str) + nodes = defaultdict() + nonterms = set() + eps_nonterms = set() + edges = [] + cnt = 0 + sz = 0 + for prod in prods: + if automata_intersec is not None: + automata = automata_intersec + else: + automata = reg2min_dfa(prod[1]) + nonterm = prod[0] + nonterms.add(nonterm) + dict = automata.to_dict() + state_values = set() + for u in dict.keys(): + state_values.add(u.value) + for symb in dict[u].keys(): + v = dict[u][symb] + state_values.add(v.value) + lst = sorted(list(state_values)) + for value in lst: + nodes[value, cnt] = sz + sz += 1 + for u in dict.keys(): + for symb in dict[u].keys(): + if symb.value == eps: + continue + v = dict[u][symb] + edges.append((nodes[(u.value, cnt)], symb, nodes[(v.value, cnt)])) + if eps in prod[1] or automata.start_state in automata.final_states: + eps_nonterms.add(nonterm) + start_set[nodes[(automata.start_state.value, cnt)]] = nonterm + for state in list(automata.final_states): + final_set[nodes[(state.value, cnt)]] = nonterm + cnt += 1 + + matrix_automata = defaultdict() + rows = defaultdict(list) + cols = defaultdict(list) + data = defaultdict(list) + + for u, symb, v in edges: + rows[symb].append(u) + cols[symb].append(v) + data[symb].append(True) + + for symb in data.keys(): + matrix_automata[symb] = scipy.sparse.csr_matrix((data[symb], (rows[symb], cols[symb])), shape=(sz, sz), dtype=bool) + + start_lst = ["" for _ in range(sz + 1)] + final_lst = ["" for _ in range(sz + 1)] + + for num, nonterm in start_set.items(): + start_lst[num] = nonterm + + for num, nonterm in final_set.items(): + final_lst[num] = nonterm + + return matrix_automata, start_lst, final_lst, edges, eps_nonterms, nonterms, sz + + +def tensor_alg(prods, graph, automata_intersec=None): + matrix_automata, start_set, final_set, edges, eps_nonterms, nonterms, m = build_automata(prods, automata_intersec) + + vert = set() + for v, _, u in graph: + vert.add(v) + vert.add(u) + + n = len(vert) + rows = defaultdict(list) + cols = defaultdict(list) + data = defaultdict(list) + + for v, symb, u in graph: + rows[symb].append(v) + cols[symb].append(u) + data[symb].append(True) + + for i in range(n): + for nonterm in eps_nonterms: + rows[nonterm].append(i) + cols[nonterm].append(i) + data[nonterm].append(True) + + matrix_graph = defaultdict() + symbols = matrix_automata.keys() | data.keys() | nonterms + for symb in symbols: + matrix_graph[symb] = scipy.sparse.csr_matrix((data[symb], (rows[symb], cols[symb])), shape=(n, n), dtype=bool) + + symbols_ = matrix_automata.keys() & matrix_graph.keys() + change = True + k = m * n + while change: + change = False + matrix_prod = scipy.sparse.csr_matrix((k, k), dtype=bool) + for symb in symbols_: + matrix_prod += scipy.sparse.kron(matrix_automata[symb], matrix_graph[symb]) + pow = k - 1 + while pow: + pow //= 2 + matrix_prod += matrix_prod * matrix_prod + r_nnz, c_nnz = matrix_prod.nonzero() + for i, j in zip(r_nnz, c_nnz): + if matrix_prod[i, j]: + s = i // n + f = j // n + if start_set[s] != "" and final_set[f] != "": + x = i % n + y = j % n + if start_set[s] == final_set[f]: + if matrix_graph[start_set[s]][x, y] == False: + change = True + matrix_graph[start_set[s]][x, y] = True + + return matrix_automata, matrix_graph, m, n + + +def solve_matrix_alg(filename_grammar, filename_graph, filename_res): + prods = to_weak_CNF(parse_grammar(filename_grammar)) + res = matrix_alg(prods, parse_graph(filename_graph)) + print_grammar(prods, filename_res) + print_res("S", res, filename_res) + + +def solve_tensor_alg(filename_grammar, filename_graph, filename_res): + matrix_automata, matrix_graph, m, n = tensor_alg(parse_ext_grammar(filename_grammar), parse_graph(filename_graph)) + print_res_tensor("S", matrix_automata, matrix_graph, m, n, filename_res) diff --git a/src/graph_lang.py b/src/graph_lang.py index 0f08ec3..f9b2c1b 100644 --- a/src/graph_lang.py +++ b/src/graph_lang.py @@ -4,6 +4,7 @@ from pyformlang.finite_automaton import NondeterministicFiniteAutomaton from cyk import parse_graph + def build_automata_from_graph(filename): automata = NondeterministicFiniteAutomaton() graph = parse_graph(filename) @@ -17,18 +18,28 @@ def build_automata_from_graph(filename): for u, symb, v in graph: automata.add_transition(State((filename, u)), Symbol(symb), State((filename, v))) + return automata.minimize() + + +def universal(): + automata = NondeterministicFiniteAutomaton() + node = State(("", 0)) + automata.add_start_state(node) + automata.add_final_state(node) + for chr in "abcdefghijklmnopqrstuvwxyz": + automata.add_transition(node, Symbol(chr), node) + + return automata + def intersec(automata1, automata2): - return automata1.get_intersection(automata2) + return automata1.get_intersection(automata2).minimize() def union(automata1, automata2): - return compl(intersec(compl(automata1), compl(automata2))) + return compl(intersec(compl(automata1), compl(automata2))).minimize() def compl(automata): - return automata.get_complement() - + return universal().get_difference(automata).minimize() -def graph_lang_intersect(prods, automata, graph): - pass diff --git a/syntax_lang.md b/syntax_lang.md index 294c62c..a5d025f 100644 --- a/syntax_lang.md +++ b/syntax_lang.md @@ -99,7 +99,8 @@ select exists from [graph1.txt] where (1) - S -> (3) ``` select count from [graph1.txt] where (2) - (a | b)* -> (_) using hellings ``` -######adding a graph expression means that the paths between the required pairs of vertices must also satisfy the condition of being in the language specified by this expression +###### adding a graph expression means that between the required pairs of vertices there must be a path belonging to the language generated by this expression + ``` select count from intersec ([graph1.txt], compl ([graph2.txt])) [graph3.txt] where (_) - S -> (3) ``` diff --git a/test/graph_queries_tests.py b/test/graph_queries_tests.py index f8d09e7..6266ef9 100644 --- a/test/graph_queries_tests.py +++ b/test/graph_queries_tests.py @@ -471,3 +471,202 @@ def test_select_using_tensors(tmp_path, capsys): assert out == "0 2\n1 2\n2 2\n" assert err == "" + + +def test_select_graph_lang1(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 a 2\n2 b 3") + file_graph2.write_text("0 b 1\n1 a 2\n2 c 3") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select get from intersec ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n1 1\n1 2\n2 2\n3 3\n" + assert err == "" + + +def test_select_graph_lang2(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 a 2\n2 b 3") + file_graph2.write_text("0 a 1\n1 b 2\n2 c 3") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select get from intersec ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n0 2\n1 1\n1 2\n2 2\n3 3\n" + assert err == "" + + +def test_select_graph_lang3(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 0") + file_graph2.write_text("0 a 1\n1 b 2\n2 c 3\n3 a 0") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3\n3 b 0") + file_script.write_text("select get from intersec ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (1) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "1 1\n1 2\n1 3\n" + assert err == "" + + +def test_select_graph_lang4(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 b 1\n1 b 2\n2 c 0") + file_graph2.write_text("0 a 1\n1 b 2\n2 c 3\n3 a 0") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3\n3 b 0") + file_script.write_text("select get from intersec ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - a | b | c -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "1 2\n2 3\n3 0\n" + assert err == "" + + +def test_select_graph_lang5(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 c 2\n2 c 3") + file_graph2.write_text("0 b 1\n1 c 2") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("select get from union ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n1 1\n1 2\n1 3\n2 2\n2 3\n3 3\n" + assert err == "" + + +def test_select_graph_lang6(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 c 2\n2 c 3") + file_graph2.write_text("0 b 1\n1 c 2") + file_graph3.write_text("0 a 1\n1 b 2\n2 c 3") + file_script.write_text("S = a | b c;\nselect get from union ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 1\n1 3\n" + assert err == "" + + +def test_select_graph_lang7(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3\n3 c 0") + file_graph2.write_text("0 a 1\n1 a 2") + file_graph3.write_text("0 a 1\n1 a 2\n2 a 3\n2 b 1\n1 c 0") + file_script.write_text("S = a | b c | ();\nselect get from union ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "]) [" + os.path.normpath(file_graph3) + "] where (_) - a S -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 1\n0 2\n1 0\n1 2\n1 3\n2 3\n" + assert err == "" + + +def test_select_graph_lang7(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3") + file_graph2.write_text("0 a 1\n1 a 2\n2 c 3") + file_script.write_text("select get from compl ([" + os.path.normpath(file_graph1) + "]) \ + [" + os.path.normpath(file_graph2) + "] where (_) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 2\n0 3\n1 3\n" + assert err == "" + + +def test_select_graph_lang8(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3") + file_graph2.write_text("0 a 1\n1 a 2\n2 c 3") + file_script.write_text("S = (a | b | c)*;\nselect get from compl ([" + os.path.normpath(file_graph1) + "]) \ + [" + os.path.normpath(file_graph2) + "] where (_) - S -> (3);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 3\n1 3\n" + assert err == "" + + +def test_select_graph_lang9(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3") + file_graph2.write_text("0 a 1\n1 b 2\n2 c 3\n3 a 4") + file_script.write_text("select get from compl ([" + os.path.normpath(file_graph1) + "]) \ + [" + os.path.normpath(file_graph2) + "] where (_) - a | b c -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "" + assert err == "" + + +def test_select_graph_lang10(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3\n3 c 0") + file_graph2.write_text("0 a 1\n1 a 2") + file_graph3.write_text("0 a 1\n1 a 2\n2 a 3\n2 b 1\n1 c 0") + file_script.write_text("select get from union ([" + os.path.normpath(file_graph1) + "], intersec ([" \ + + os.path.normpath(file_graph2) + "], [" + os.path.normpath(file_graph3) + "])) [" \ + + os.path.normpath(file_graph3) + "] where (0) - (a | b | c)* -> (_);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "0 0\n0 1\n0 2\n" + assert err == "" + + +def test_select_graph_lang11(tmp_path, capsys): + file_script = tmp_path / "script.txt" + file_graph1 = tmp_path / "graph1.txt" + file_graph2 = tmp_path / "graph2.txt" + file_graph3 = tmp_path / "graph3.txt" + file_graph1.write_text("0 a 1\n1 b 2\n2 c 3\n3 c 0") + file_graph2.write_text("0 a 1\n1 a 2\n1 b 2") + file_graph3.write_text("0 a 1\n1 a 2\n2 a 3\n2 b 1\n1 c 0") + file_script.write_text("select get from compl (intersec ([" + os.path.normpath(file_graph1) + "], [" + \ + os.path.normpath(file_graph2) + "])) [" + os.path.normpath(file_graph3) + "] where (_) - a | b | c -> (0);") + process(get_stream(True, os.path.normpath(file_script))) + out, err = capsys.readouterr() + + assert out == "1 0\n" + assert err == ""