Skip to content

Commit

Permalink
feature (cfa): if, while, do-while, for, switch
Browse files Browse the repository at this point in the history
  • Loading branch information
Andreas committed Mar 19, 2022
1 parent 60509ee commit 0479202
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 96 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
./pylintrc.txt
examples/
/graphs

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/graphs

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
40 changes: 35 additions & 5 deletions src/cfa/cfa.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from ts import Node
from src.ts.tree import Tree
from src.ts.node import Node
from typing import Dict, List, Iterable
from queue import Queue
import graphviz

class CFANode:
def __init__(self, node: Node, location: int = -1) -> None:
Expand Down Expand Up @@ -60,7 +62,7 @@ def ingoing(self, destination: CFANode) -> List[CFANode]:
def ingoing_edges(self, source: CFANode) -> List[CFAEdge]:
return self._ingoing_edges[source]

def branch(self, source: CFANode, destination: CFANode) -> None:
def branch(self, source: CFANode, destination: CFANode, label: str = None) -> None:
if source not in self._nodes:
self._nodes.append(source)
self._outgoing_edges[source] = list()
Expand All @@ -70,7 +72,7 @@ def branch(self, source: CFANode, destination: CFANode) -> None:
self._outgoing_edges[destination] = list()
self._ingoing_edges[destination] = list()

edge: CFAEdge = CFAEdge(source, destination)
edge: CFAEdge = CFAEdge(source, destination, label)
self._outgoing_edges[source].append(edge)
self._ingoing_edges[destination].append(edge)

Expand All @@ -84,9 +86,8 @@ def remove(self, source: CFANode) -> None:
# b -> a
for ingoing in self._ingoing_edges[source]:
for outgoing in self._outgoing_edges[source]:
self.branch(ingoing.source, outgoing.destination)
self.branch(ingoing.source, outgoing.destination, ingoing.label)


for node in self._ingoing_edges:
for edge in self._ingoing_edges[node]:
if edge.source is source or edge.destination is source:
Expand All @@ -113,6 +114,35 @@ def replace(self, before: CFANode, after: CFANode) -> None:
del self._ingoing_edges[before]
del self._outgoing_edges[before]

def draw(self, tree: Tree, name: str) -> graphviz.Digraph:
dot = graphviz.Digraph(name)

def node_name(cfa_node: CFANode) -> str:
node: Node = cfa_node.node
if node is None: return f'NULL'
location: int = cfa_node.node.end_byte
return f'l{location} {tree.contents_of(node).replace(":", "")}'

visited: List[CFANode] = list()
visited_edges: List[CFAEdge] = list()
queue: Queue[CFANode] = Queue()
queue.put(self.root)
while not queue.empty():
current: CFANode = queue.get()
visited.append(current)

for edge in self.outgoing_edges(current):
if edge.destination not in visited:
queue.put(edge.destination)
if edge not in visited_edges:
visited_edges.append(edge)
dot.edge(
node_name(edge.source),
node_name(edge.destination),
edge.label
)
return dot

def breadth_first_traverse(self) -> Iterable[CFANode]:
queue: Queue[CFANode] = Queue()
visited: List[CFANode] = list()
Expand Down
108 changes: 108 additions & 0 deletions src/cfa/test_graphs_graphics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import unittest
from os import linesep
from typing import List, Tuple

from src.cfa import CFA

from src.ts import (
LanguageLibrary,
Parser,
Query,
Tree,
TreeCFAVisitor,
)


class TestGraphsGraphics(unittest.TestCase):
def setUp(self) -> None:
LanguageLibrary.build()
self._language = LanguageLibrary.c()
self._parser = Parser.create_with_language(self._language)

self._compound_assignment_query: Query = self._language.query(self._language.syntax.query_compound_assignment)
self._assignment_query: Query = self._language.query(self._language.syntax.query_assignment)
self._binary_expression_query: Query = self._language.query(self._language.syntax.query_binary_expression)
return super().setUp()

def test_create_graphs_graphics(self) -> None:
programs: List[Tuple[str, str]] = [
("if_1", "a=1; if(a==2) { }"),
("if_2", "a=1; if(a==2) { } else { }"),
("if_3", "a=1; if(a==2) { } else { } a=2;"),
("if_4", "a=1; if(a==2) { } else if(a==3) { } else { }"),
("if_5", "a=1; if(a==2) { } else if(a==3) { } a=2;"),
("if_6", "a=1; if(a==1) { a=2; }"),
("if_7", "a=1; if(a==1) { a=2; } a=3;"),
("if_8", "a=1; if(a==1) { a=2; } else { a=3; }"),
("if_9", "a=1; if(a==1) { a=2; } else { a=3; } a=4;"),
("if_10", "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } a=4;"),
("if_11", "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } a=4;"),
("if_12", "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } else { a=4; } a=5; a=6;"),
("if_13", "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } else if(a==3) { a=4; } a=5;"),
("if_14", "a=1; if(a==1) { a=2; } else if(a==2) { } else if(a==3) { a=4; } else if(a==4) { a=5; } else { a=6; } a=7;"),
("if_15", "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } else if(a==3) { a=4; } else if(a==4) { a=5; } else { a=6; } a=7;"),
("if_16", "a=1; if(a==1) { a=2; } a=3; if(a==2) { a=2; } a=3; if(a==3) { a=2; } a=3;"),
("while_1", "while(a==1) { }"),
("while_2", "while(a==1) { } a=3;"),
("while_3", "while(a==1) { a=2; } a=3;"),
("while_4", "while(a==1) { a=1; a=2; a=3; } a=4;"),
("while_4", "while(a==1) { a=1; a=2; a=3; } a=4;"),
("while_5", "while(a==1) { if(a==1) { a=1; } a=2; a=3; a=4; a=5; } a=6;"),
("while_6", "while(a==1) { if(a==1) { a=1; } else { a=2; } a=3; } a=4;"),
("while_7", "while(a==1) { if(a==1) { a=1; } else if(a==2) { a=2; } else { a=3; } a=4; } a=5;"),
("while_8", "while(a==1) { if(a==1) { a=1; } a=2; if(a==2) { a=3; } a=4; a=5; a=6; } a=7;"),
("do_while_1", "do { a=1; } while(a==1); a=2;"),
("do_while_2", "do { if(a==1) { a=1; } a=2; } while(a==1); a=2;"),
("do_while_3", "do { a=0; if(a==1) { a=1; } a=2; } while(a==1); a=2;"),
("for_1", "for (int i=0; i<5; ++i) { a=2; } a=3;"),
("switch_1", """
switch (a)
{
case 1: a=1;
}
"""),
("switch_2", """
switch (a)
{
case 3: { a=2; }
}
"""),
("switch_3", """
switch (a)
{
case 1: a=1;
case 2: a=1;
case 3: { a=2; }
default: a=3;
}
"""),
("switch_4", """
switch (a)
{
case 1:
case 2: a=1;
case 3: { a=2; }
default: a=3;
}
"""),
("switch_5", """
switch (a)
{
case 1:
case 2: a=1;
case 3: { a=2; }
default: a=3;
}
a=10;
""")
]
for program in programs:
name: str = program[0]
prog: str = program[1]
tree: Tree = self._parser.parse(prog)
visitor: TreeCFAVisitor = TreeCFAVisitor()
cfa: CFA = visitor.create(tree.root_node)
dot = cfa.draw(tree, name)
dot.save(directory="graphs")

self.assertEqual(' '.join(visitor._order), None)
46 changes: 2 additions & 44 deletions src/ts/test_tree_cursor_visitor.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@

from platform import node
import unittest
from typing import Iterable, List
import graphviz

from src.cfa import CFA
from src.cfa.cfa import CFAEdge, CFANode
from queue import Queue
from src.cfa.cfa import CFANode

from . import (
from src.ts import (
Node,
LanguageLibrary,
Parser,
Expand Down Expand Up @@ -226,44 +222,6 @@ def test_tree_cfa_creation_one_if_else_statement(self) -> None:
self.assertEqual(next_true.node.type, "expression_statement")
self.assertEqual(tree.contents_of(next_true.node), "a = 4;")

def test_tree_cfa_creation_one_if_elseif_else_statement(self) -> None:
tree: Tree = self._parser.parse(
# "a=1; if(a==2) { }"
# "a=1; if(a==2) { } else { }"
# "a=1; if(a==2) { } else { } a=2;"
# "a=1; if(a==2) { } else if(a==3) { } else { }"
"a=1; if(a==2) { } else if(a==3) { } a=2;"
# "a=1; if(a==1) { a=2; }"
# "a=1; if(a==1) { a=2; } a=3;"
# "a=1; if(a==1) { a=2; } else { a=3; }"
# "a=1; if(a==1) { a=2; } else { a=3; } a=4;"
# "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } a=4;"
# "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } a=4;"
# "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } else if(a==3) { a=4; } a=5;"
# "a=1; if(a==1) { a=2; } else if(a==2) { a=3; } else if(a==3) { a=4; } else if(a==4) { a=5; } else { a=6; } a=7;"
)
visitor: TreeCFAVisitor = TreeCFAVisitor()
cfa: CFA = visitor.create(tree.root_node)

def name(cfa_node: CFANode) -> str:
node: Node = cfa_node.node
if node is None: return f'l{cfa_node.location}'
return f'l{cfa_node.location} {tree.contents_of(node)}'

dot = graphviz.Digraph('graph')
visited: List[CFANode] = list()
queue: Queue[CFANode] = Queue()
queue.put(cfa.root)
while not queue.empty():
current: CFANode = queue.get()
visited.append(current)

for edge in cfa.outgoing_edges(current):
if edge.destination not in visited:
queue.put(edge.destination)
dot.edge(name(edge.source), name(edge.destination))
dot.save()

def test_tree_visitor_for_cfa_one_if_elseif_else_statement(self) -> None:
tree: Tree = self._parser.parse(
"if (a == 1) { a = 2; } else if (a == 2) { } else { a = 3; }"
Expand Down
Loading

0 comments on commit 0479202

Please sign in to comment.