From cfd675d4fde2e6a24682669d9708e91793fc0873 Mon Sep 17 00:00:00 2001 From: Matheus Bernardo Date: Fri, 13 Aug 2021 20:26:40 -0300 Subject: [PATCH 1/4] Implements CFG as tuple of Labels (Int), small improvements, adds gitignore and remove lib --- .gitignore | 244 ++++++++++++++++++ build.sbt | 2 - project/build.properties | 1 + .../scala/br/unb/cic/wlang/CFGBuilder.scala | 77 +++--- .../unb/cic/wlang/CFGBuilderBuilderTest.scala | 27 +- 5 files changed, 305 insertions(+), 46 deletions(-) create mode 100644 .gitignore create mode 100644 project/build.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5deb5fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,244 @@ +project/target +project/.bloop/ +project/metals.sbt +project/project/* +target +.env +.jvmopts +.sbtopts +.metals/ +.vscode/ +*/.bloop/ +.bloop/ + +# Created by https://www.gitignore.io/api/java,linux,macos,scala,eclipse,intellij+all +# Edit at https://www.gitignore.io/?templates=java,linux,macos,scala,eclipse,intellij+all + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Annotation Processing +.apt_generated + +.sts4-cache/ + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# JetBrains templates +**___jb_tmp___ + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Scala ### +.bsp + +# End of https://www.gitignore.io/api/java,linux,macos,scala,eclipse,intellij+all diff --git a/build.sbt b/build.sbt index 8977eaa..e454fbc 100644 --- a/build.sbt +++ b/build.sbt @@ -6,5 +6,3 @@ scalaVersion := "2.13.6" libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.0" % "test" libraryDependencies += "org.scalatest" %% "scalatest-featurespec" % "3.2.0" % "test" - -libraryDependencies += "org.scala-graph" %% "graph-core" % "1.13.2" \ No newline at end of file diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..9edb75b --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.5.4 diff --git a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala index 6cef282..5b52b00 100644 --- a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala +++ b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala @@ -1,27 +1,14 @@ package br.unb.cic.wlang -/** - * The root of the hierarchy of the - * control-flow graph nodes. - */ -abstract class GraphNode - - -/** - * Represents a real node from the function or program statements - * - * @param stmt the statement of a program or function - */ -case class Node(stmt: Stmt) extends GraphNode - - /** * An Scala object responsible for building control * flow graphs from a While program. */ object CFGBuilder { - type CFG = Set[(GraphNode, GraphNode)] + type Label = Int + type Block = (Stmt, Label) + type CFG = Set[(Label, Label)] /** * Builds a control flow graph from a given While program. * @@ -38,34 +25,64 @@ object CFGBuilder { * * (for { from <- finalStmt(s1) } yield (from, initStatement(s2))) */ - private def flow(stmt: Stmt): CFG = { + private def flow(stmt: Stmt): CFG = stmt match { case Assignment(_, _, _) => Set.empty case Skip(_) => Set.empty - case Sequence(s1, s2) => flow(s1) union flow(s2) union finalStmt(s1).map(from => (from, initStmt(s2))) - case IfThenElse(_, s1, s2, _) => flow(s1) union flow(s2) union Set((Node(stmt), initStmt(s1)), (Node(stmt), initStmt(s2))) - case While(_, s, _) => flow(s) union Set((Node(stmt), initStmt(s))) union finalStmt(s).map(from => (from, Node(stmt))) + case Sequence(s1, s2) => + flow(s1) union flow(s2) union finalLabel(s1).map(from => (from, initLabel(s2))) + case IfThenElse(_, s1, s2, l) => + flow(s1) union flow(s2) union Set((l, initLabel(s1)), (l, initLabel(s2))) + case While(_, s, l) => + flow(s) union Set((l, initLabel(s))) union finalLabel(s).map(from => (from, l)) } - } + /* - * Returns the first statement of a given statement. + * Returns the first label of a given statement. * * @see Section 2.1 of Principles of Program Analysis */ - private def initStmt(stmt: Stmt) : GraphNode = stmt match { - case Sequence(s1, _) => initStmt(s1) - case _ => Node(stmt) + private def initLabel(stmt: Stmt) : Label = stmt match { + case Assignment(_,_, label) => label + case Skip(label) => label + case Sequence(s1, _) => initLabel(s1) + case IfThenElse(_,_, _, label) => label + case While(_,_, label) => label } /* - * Returns the last statement of a given statement. + * Returns the set of last labels of a given statement. * * @see Section 2.1 of Principles of Program Analysis */ - private def finalStmt(stmt: Stmt) : Set[GraphNode] = stmt match { - case Sequence(_, s2) => finalStmt(s2) - case IfThenElse(_, thenStmt, elseStmt, _) => finalStmt(thenStmt) union finalStmt(elseStmt) - case _ => Set(Node(stmt)) + private def finalLabel(stmt: Stmt) : Set[Label] = stmt match { + case Assignment(_,_, label) => Set(label) + case Skip(label) => Set(label) + case Sequence(_, s2) => finalLabel(s2) + case IfThenElse(_,s1, s2, _) => finalLabel(s1) union finalLabel(s2) + case While(_,_, label) => Set(label) } + + + /** + * Returns the set of elementary blocks given a statement + * + * @see Section 2.1 of Principles of Program Analysis + */ + private def blocks(stmt: Stmt) : Set[Block] = stmt match { + case Assignment(name, exp, label) => Set((stmt, label)) + case Skip(label) => Set((stmt, label)) + case Sequence(s1, s2) => blocks(s1) union blocks(s2) + case IfThenElse(condition, thenStmt, elseStmt, label) => + Set((stmt, label)) union blocks(thenStmt) union blocks(elseStmt) + case While(condition, stmt, label) => Set((stmt, label)) union blocks(stmt) + } + + /** + * Returns the set of labels occurring in a program + * + * @see Section 2.1 of Principles of Program Analysis + */ + private def labels(stmt: Stmt): Set[Label] = blocks(stmt).map(_._2) } diff --git a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala index b76135e..77fdcbb 100644 --- a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala +++ b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala @@ -7,12 +7,11 @@ class CFGBuilderBuilderTest extends AnyFunSuite { test("Test simple CFG") { val stmt = Assignment("x", Const(4), 1) val program = WhileProgram(stmt) - val sn = Node(stmt) - val g = CFGBuilder.build(program) + val resultCFG = CFGBuilder.build(program) - val expected = Set() + val expectedCFG = Set.empty - assert(expected == g) + assert(expectedCFG == resultCFG) } test("Test factorial CFG") { @@ -23,19 +22,19 @@ class CFGBuilderBuilderTest extends AnyFunSuite { val w1 = While(GT(Var("y"), Const(1)), Sequence(d3, d4), 3) val d5 = Assignment("y", Const(0), 6) - val p = WhileProgram(Sequence(d1, Sequence(d2, Sequence(w1, d5)))) + val program = WhileProgram(Sequence(d1, Sequence(d2, Sequence(w1, d5)))) - val g = CFGBuilder.build(p) + val resultCFG = CFGBuilder.build(program) - val expected: Set[(Node, Node)] = - Set((Node(d1), Node(d2)) - ,(Node(d2), Node(w1)) - ,(Node(w1), Node(d3)) - ,(Node(d3), Node(d4)) - ,(Node(d4), Node(w1)) - ,(Node(w1), Node(d5))) + val expectedCFG = + Set((d1.label, d2.label) + ,(d2.label, w1.label) + ,(w1.label, d3.label) + ,(d3.label, d4.label) + ,(d4.label, w1.label) + ,(w1.label, d5.label)) - assert(expected == g) + assert(expectedCFG == resultCFG) } } From a224aa42fb46a316784c5eec046d15118698310b Mon Sep 17 00:00:00 2001 From: Matheus Bernardo Date: Fri, 20 Aug 2021 23:45:59 -0300 Subject: [PATCH 2/4] Implements Available Expression in purely functional --- .../unb/cic/wlang/AvailableExpression.scala | 82 ++++++++++++++ .../scala/br/unb/cic/wlang/CFGBuilder.scala | 98 ++++++----------- src/main/scala/br/unb/cic/wlang/Syntax.scala | 104 ++++++++++++++---- .../cic/wlang/AvailableExpressionTest.scala | 38 +++++++ .../unb/cic/wlang/CFGBuilderBuilderTest.scala | 39 +++++-- 5 files changed, 270 insertions(+), 91 deletions(-) create mode 100644 src/main/scala/br/unb/cic/wlang/AvailableExpression.scala create mode 100644 src/test/scala/br/unb/cic/wlang/AvailableExpressionTest.scala diff --git a/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala b/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala new file mode 100644 index 0000000..1d5fc3a --- /dev/null +++ b/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala @@ -0,0 +1,82 @@ +package br.unb.cic.wlang + +import br.unb.cic.wlang.CFGBuilder._ +import br.unb.cic.wlang.WhileProgram._ + +object AvailableExpression { + def process(program: WhileProgram): Abstraction = { + val bottom = nonTrivialExpressions(program) + val initialExit = + labels(program).foldLeft(Map[Int, Set[AExp]]()) { (m, label) => m + (label -> bottom) } + + val abstraction = Abstraction(Map.empty, initialExit) + process(program, abstraction) + } + + private def process(program: WhileProgram, abstraction: Abstraction): Abstraction = { + val (newAbstraction, continue) = iterate(labels(program), program, abstraction) + if (continue) { + process(program, newAbstraction) + } else { + newAbstraction + } + } + + private def iterate(labels: Set[Int], program: WhileProgram, abstraction: Abstraction): (Abstraction, Boolean) = { + val newAbstraction = labels.foldLeft(abstraction) { (abstraction, label) => { + val newAbstractionForLabel = generateNewAbstractionForLabel(label, program,abstraction) + val newEntry = newAbstractionForLabel._1 + val newExit = newAbstractionForLabel._2 + Abstraction(abstraction.entry + newEntry, abstraction.exit + newExit) + } + } + (newAbstraction, abstraction != newAbstraction) + } + private def generateNewAbstractionForLabel(label: Int, program: WhileProgram, abstraction: Abstraction): ((Int, Set[AExp]), (Int, Set[AExp])) = { + val entry = generateEntry(label, program, abstraction) + val exit = generateExit(label, program, Map(label -> entry)) + (label -> entry, label -> exit) + } + + private def kill(block: Block, nonTrivialExpressions: Set[AExp]): Set[AExp] = block match { + case Assignment(name, _, _) => nonTrivialExpressions.filter(exp => expHasVariable(name, exp)) + case Skip(_) => Set.empty + case Condition(_, _) => Set.empty + } + + private def gen(block: Block): Set[AExp] = block match { + case Assignment(name, exp, _) => nonTrivialExpressions(exp).filterNot(exp => expHasVariable(name, exp)) + case Skip(_) => Set.empty + case Condition(exp, _) => nonTrivialExpressions(exp) + } + + private def expHasVariable(name: String, exp: AExp): Boolean = exp match { + case Var(n) => n == name + case Const(_) => false + case OpArith(_, a1, a2) => expHasVariable(name, a1) || expHasVariable(name, a2) + } + + private def generateExit(label: Int, program: WhileProgram, entry: Map[Int, Set[AExp]]) = { + val calculatedBlock = block(label, program.stmt).get + val nonTrivialExpressions = WhileProgram.nonTrivialExpressions(program) + val exit = + (entry(label) diff kill(calculatedBlock, nonTrivialExpressions)) union gen(calculatedBlock) + exit + } + + private def generateEntry(label: Int, program: WhileProgram, abstraction: Abstraction): Set[AExp] = { + + if (label == initLabel(program.stmt)) { + Set.empty + } else { + val sets = (for { + (from, to) <- flow(program.stmt) + if to == label + } yield abstraction.exit(from)).toList + sets.foldLeft[Set[AExp]](sets.head)((s1,s2) => s1 intersect s2) + } + + } + + case class Abstraction(entry: Map[Int, Set[AExp]], exit: Map[Int, Set[AExp]]) +} \ No newline at end of file diff --git a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala index 5b52b00..f5d7a85 100644 --- a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala +++ b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala @@ -1,54 +1,31 @@ package br.unb.cic.wlang - /** - * An Scala object responsible for building control - * flow graphs from a While program. - */ + * An Scala object responsible for building control + * flow graphs from a While program. + */ object CFGBuilder { - type Label = Int - type Block = (Stmt, Label) - type CFG = Set[(Label, Label)] + type CFG = Set[(Int, Int)] + /** - * Builds a control flow graph from a given While program. - * - * @param program a While program - * - * @return The control-flow graph of the While program - */ + * Builds a control flow graph from a given While program. + * + * @param program a While program + * @return The control-flow graph of the While program + */ def build(program: WhileProgram): CFG = flow(program.stmt) - /* - * The "core" of the algorithm for building - * control-flow graphs. Here we use pattern - * matching over the different statements. - * - * (for { from <- finalStmt(s1) } yield (from, initStatement(s2))) - */ - private def flow(stmt: Stmt): CFG = - stmt match { - case Assignment(_, _, _) => Set.empty - case Skip(_) => Set.empty - case Sequence(s1, s2) => - flow(s1) union flow(s2) union finalLabel(s1).map(from => (from, initLabel(s2))) - case IfThenElse(_, s1, s2, l) => - flow(s1) union flow(s2) union Set((l, initLabel(s1)), (l, initLabel(s2))) - case While(_, s, l) => - flow(s) union Set((l, initLabel(s))) union finalLabel(s).map(from => (from, l)) - } - - /* * Returns the first label of a given statement. * * @see Section 2.1 of Principles of Program Analysis */ - private def initLabel(stmt: Stmt) : Label = stmt match { - case Assignment(_,_, label) => label + def initLabel(stmt: Stmt): Int = stmt match { + case Assignment(_, _, label) => label case Skip(label) => label case Sequence(s1, _) => initLabel(s1) - case IfThenElse(_,_, _, label) => label - case While(_,_, label) => label + case IfThenElse(Condition(_, label), _, _) => label + case While(Condition(_, label), _) => label } /* @@ -56,33 +33,30 @@ object CFGBuilder { * * @see Section 2.1 of Principles of Program Analysis */ - private def finalLabel(stmt: Stmt) : Set[Label] = stmt match { - case Assignment(_,_, label) => Set(label) + def finalLabel(stmt: Stmt): Set[Int] = stmt match { + case Assignment(_, _, label) => Set(label) case Skip(label) => Set(label) case Sequence(_, s2) => finalLabel(s2) - case IfThenElse(_,s1, s2, _) => finalLabel(s1) union finalLabel(s2) - case While(_,_, label) => Set(label) + case IfThenElse(_, s1, s2) => finalLabel(s1) union finalLabel(s2) + case While(Condition(_, label), _) => Set(label) } - - /** - * Returns the set of elementary blocks given a statement - * - * @see Section 2.1 of Principles of Program Analysis - */ - private def blocks(stmt: Stmt) : Set[Block] = stmt match { - case Assignment(name, exp, label) => Set((stmt, label)) - case Skip(label) => Set((stmt, label)) - case Sequence(s1, s2) => blocks(s1) union blocks(s2) - case IfThenElse(condition, thenStmt, elseStmt, label) => - Set((stmt, label)) union blocks(thenStmt) union blocks(elseStmt) - case While(condition, stmt, label) => Set((stmt, label)) union blocks(stmt) - } - - /** - * Returns the set of labels occurring in a program - * - * @see Section 2.1 of Principles of Program Analysis - */ - private def labels(stmt: Stmt): Set[Label] = blocks(stmt).map(_._2) + /* + * The "core" of the algorithm for building + * control-flow graphs. Here we use pattern + * matching over the different statements. + * + * (for { from <- finalStmt(s1) } yield (from, initStatement(s2))) + */ + def flow(stmt: Stmt): CFG = + stmt match { + case Assignment(_, _, _) => Set.empty + case Skip(_) => Set.empty + case Sequence(s1, s2) => + flow(s1) union flow(s2) union finalLabel(s1).map(from => (from, initLabel(s2))) + case IfThenElse(Condition(_, l), s1, s2) => + flow(s1) union flow(s2) union Set((l, initLabel(s1)), (l, initLabel(s2))) + case While(Condition(_, l), s) => + flow(s) union Set((l, initLabel(s))) union finalLabel(s).map(from => (from, l)) + } } diff --git a/src/main/scala/br/unb/cic/wlang/Syntax.scala b/src/main/scala/br/unb/cic/wlang/Syntax.scala index da398ef..94517f0 100644 --- a/src/main/scala/br/unb/cic/wlang/Syntax.scala +++ b/src/main/scala/br/unb/cic/wlang/Syntax.scala @@ -1,42 +1,104 @@ package br.unb.cic.wlang /** - * Abstract representation of a While Program. - * - * @param stmt The program main statement. - */ + * Abstract representation of a While Program. + * + * @param stmt The program main statement. + */ case class WhileProgram(stmt: Stmt) +object WhileProgram { + + def nonTrivialExpressions(program: WhileProgram): Set[AExp] = + blocks(program.stmt).flatMap(block => nonTrivialExpressions(block)) + + def nonTrivialExpressions(block: Block): Set[AExp] = block match { + case Assignment(_, exp, _) => nonTrivialExpressions(exp) + case Skip(_) => Set.empty + case Condition(exp, _) => nonTrivialExpressions(exp) + } + + def nonTrivialExpressions(exp: Exp): Set[AExp] = exp match { + case aExp: AExp => aExp match { + case Var(_) => Set.empty + case Const(_) => Set.empty + case OpArith(op, a1, a2) => Set(aExp) union nonTrivialExpressions(a1) union nonTrivialExpressions(a2) + } + case bExp: BExp => bExp match { + case True => Set.empty + case False => Set.empty + case Not(exp) => nonTrivialExpressions(exp) + case OpBool(_, b1, b2) => nonTrivialExpressions(b1) union nonTrivialExpressions(b2) + case OpRelat(_, a1, a2) => nonTrivialExpressions(a1) union nonTrivialExpressions(a2) + } + } + + def labels(program: WhileProgram): Set[Int] = labels(program.stmt) + + def block(label: Int, stmt: Stmt): Option[Block] = blocks(stmt).find(_.label == label) + + private def blocks(stmt: Stmt): Set[Block] = stmt match { + case a@Assignment(_, _, _) => Set(a) + case s@Skip(_) => Set(s) + case Sequence(s1, s2) => blocks(s1) union blocks(s2) + case IfThenElse(condition, thenStmt, elseStmt) => + blocks(thenStmt) union blocks(elseStmt) union Set(condition) + case While(condition, stmt) => blocks(stmt) union Set(condition) + } + + private def labels(stmt: Stmt): Set[Int] = stmt match { + case Assignment(_, _, label) => Set(label) + case Skip(label) => Set(label) + case Sequence(s1, s2) => labels(s1) union labels(s2) + case IfThenElse(c, s1, s2) => Set(c.label) union labels(s1) union labels(s2) + case While(c, s) => Set(c.label) union labels(s) + } +} + /* The abstract classes * - AExp: Arithmetic expressions * - BExp: Binary expressions * - Stmt: Statements */ -abstract class AExp -abstract class BExp -abstract class Stmt +sealed trait Exp + +sealed trait AExp extends Exp + +sealed trait BExp extends Exp + +sealed trait Stmt + +sealed trait Block { + def label: Int +} /* Concrete implementations of AExp */ -case class Var(name: String) extends AExp // variables -case class Const(value: Int) extends AExp // integer constants -case class Add(left: AExp, right: AExp) extends AExp // Add arithmetic operation -case class Sub(left: AExp, right: AExp) extends AExp // Sub arithmetic operation -case class Mult(lef: AExp, right: AExp) extends AExp // Mult arithmetic operation +case class Var(name: String) extends AExp // variables + +case class Const(value: Int) extends AExp // integer constants + +case class OpArith(op: String, a1: AExp, a2: AExp) extends AExp /* Concrete implementations of BExp */ case object True extends BExp + case object False extends BExp case class Not(exp: BExp) extends BExp -case class And(left: BExp, right: BExp) extends BExp -case class Or(Left: BExp, right: BExp) extends BExp -case class Eq(left: AExp, right: AExp) extends BExp -case class GT(left: AExp, right: AExp) extends BExp + +case class OpBool(op: String, b1: BExp, b2: BExp) extends BExp + +case class OpRelat(op: String, a1: AExp, a2: AExp) extends BExp /* Concrete implementations of Statements */ +case class Condition(exp: BExp, label: Int) extends Block + +case class Assignment(name: String, exp: AExp, label: Int) extends Stmt with Block + +case class Sequence(s1: Stmt, s2: Stmt) extends Stmt + +case class IfThenElse(condition: Condition, thenStmt: Stmt, elseStmt: Stmt) extends Stmt + +case class While(condition: Condition, stmt: Stmt) extends Stmt -case class Assignment(name: String, exp: AExp, label: Int) extends Stmt -case class Sequence(s1: Stmt, s2: Stmt) extends Stmt // s1;s2 -case class IfThenElse(condition: BExp, thenStmt: Stmt, elseStmt: Stmt, label: Int) extends Stmt -case class While(condition: BExp, stmt: Stmt, label: Int) extends Stmt -case class Skip(label: Int) extends Stmt +case class Skip(label: Int) extends Stmt with Block diff --git a/src/test/scala/br/unb/cic/wlang/AvailableExpressionTest.scala b/src/test/scala/br/unb/cic/wlang/AvailableExpressionTest.scala new file mode 100644 index 0000000..3041221 --- /dev/null +++ b/src/test/scala/br/unb/cic/wlang/AvailableExpressionTest.scala @@ -0,0 +1,38 @@ +package br.unb.cic.wlang + +import org.scalatest.funsuite.AnyFunSuite + +class AvailableExpressionTest extends AnyFunSuite { + test("example 2.4") { + val exprASumB = OpArith("Sum", Var("a"), Var("b")) + val exprAMulB = OpArith("Mul", Var("a"), Var("b")) + val exprASum1 = OpArith("Sum", Var("a"), Const(1)) + + val d1 = Assignment("x", exprASumB, 1) + val d2 = Assignment("y", exprAMulB, 2) + val d4 = Assignment("a", exprASum1, 4) + val d5 = Assignment("x", exprASumB, 5) + val w3 = While(Condition(OpRelat("GT", Var("y"), exprASumB), 3), Sequence(d4, d5)) + + val program = WhileProgram(Sequence(d1, Sequence(d2, w3))) + val entry: Map[Int, Set[AExp]] = Map( + 1 -> Set.empty, + 2 -> Set(exprASumB), + 3 -> Set(exprASumB), + 4 -> Set(exprASumB), + 5 -> Set.empty + ) + + val exit: Map[Int, Set[AExp]] = Map( + 1 -> Set(exprASumB), + 2 -> Set(exprASumB, exprAMulB), + 3 -> Set(exprASumB), + 4 -> Set.empty, + 5 -> Set(exprASumB) + ) + val expectedAbstraction = AvailableExpression.Abstraction(entry, exit) + val abstraction = AvailableExpression.process(program) + + assert(abstraction == expectedAbstraction) + } +} \ No newline at end of file diff --git a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala index 77fdcbb..8dd1229 100644 --- a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala +++ b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala @@ -17,9 +17,9 @@ class CFGBuilderBuilderTest extends AnyFunSuite { test("Test factorial CFG") { val d1 = Assignment("y", Var("x"), 1) val d2 = Assignment("z", Const(1), 2) - val d3 = Assignment("z", Mult(Var("z"), Var("y")), 4) - val d4 = Assignment("y", Sub(Var("y"), Const(1)), 5) - val w1 = While(GT(Var("y"), Const(1)), Sequence(d3, d4), 3) + val d3 = Assignment("z", OpArith("Mul", Var("z"), Var("y")), 4) + val d4 = Assignment("y", OpArith("Sub", Var("y"), Const(1)), 5) + val w1 = While(Condition(OpRelat("GT",Var("y"), Const(1)), 3), Sequence(d3, d4)) val d5 = Assignment("y", Const(0), 6) val program = WhileProgram(Sequence(d1, Sequence(d2, Sequence(w1, d5)))) @@ -28,13 +28,36 @@ class CFGBuilderBuilderTest extends AnyFunSuite { val expectedCFG = Set((d1.label, d2.label) - ,(d2.label, w1.label) - ,(w1.label, d3.label) - ,(d3.label, d4.label) - ,(d4.label, w1.label) - ,(w1.label, d5.label)) + , (d2.label, w1.condition.label) + , (w1.condition.label, d3.label) + , (d3.label, d4.label) + , (d4.label, w1.condition.label) + , (w1.condition.label, d5.label)) assert(expectedCFG == resultCFG) } + test("Test AE book CFG") { + val exprASumB = OpArith("Sum", Var("a"), Var("b")) + val exprAMulB = OpArith("Mul", Var("a"), Var("b")) + val exprASum1 = OpArith("Sum", Var("a"), Const(1)) + val d1 = Assignment("x", exprASumB, 1) + val d2 = Assignment("y", exprAMulB, 2) + val d4 = Assignment("a", exprASum1, 4) + val d5 = Assignment("x", exprASum1, 5) + val w3 = While(Condition(OpRelat("GT", Var("y"), exprASumB), 3), Sequence(d4, d5)) + + val program = WhileProgram(Sequence(d1, Sequence(d2, w3))) + + val resultCFG = CFGBuilder.build(program) + + val expectedCFG = + Set((d1.label, d2.label) + , (d2.label, w3.condition.label) + , (w3.condition.label, d4.label) + , (d4.label, d5.label) + , (d5.label, w3.condition.label)) + + assert(expectedCFG == resultCFG) + } } From f9635858d0704afe2faff72bdfbdd97ad8d11a4d Mon Sep 17 00:00:00 2001 From: Matheus Bernardo Date: Tue, 24 Aug 2021 10:36:04 -0300 Subject: [PATCH 3/4] Implements Reverse Flow --- .../scala/br/unb/cic/wlang/CFGBuilder.scala | 27 ++++++++++++------ .../unb/cic/wlang/CFGBuilderBuilderTest.scala | 28 +++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala index f5d7a85..afbfe62 100644 --- a/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala +++ b/src/main/scala/br/unb/cic/wlang/CFGBuilder.scala @@ -1,20 +1,28 @@ package br.unb.cic.wlang /** - * An Scala object responsible for building control - * flow graphs from a While program. - */ + * An Scala object responsible for building control + * flow graphs from a While program. + */ object CFGBuilder { type CFG = Set[(Int, Int)] /** - * Builds a control flow graph from a given While program. - * - * @param program a While program - * @return The control-flow graph of the While program - */ + * Builds a control flow graph from a given While program. + * + * @param program a While program + * @return The control-flow graph of the While program + */ def build(program: WhileProgram): CFG = flow(program.stmt) + /** + * Builds a control flow graph from a given While program. + * + * @param program a While program + * @return The control-flow graph of the While program + */ + def buildR(program: WhileProgram): CFG = flowR(program.stmt) + /* * Returns the first label of a given statement. * @@ -59,4 +67,7 @@ object CFGBuilder { case While(Condition(_, l), s) => flow(s) union Set((l, initLabel(s))) union finalLabel(s).map(from => (from, l)) } + + def flowR(stmt: Stmt): CFG = + flow(stmt).map({ case (from, to) => (to, from)}) } diff --git a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala index 8dd1229..20683a4 100644 --- a/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala +++ b/src/test/scala/br/unb/cic/wlang/CFGBuilderBuilderTest.scala @@ -60,4 +60,32 @@ class CFGBuilderBuilderTest extends AnyFunSuite { assert(expectedCFG == resultCFG) } + test("Build reverse CFG example 2.8") { + val exprAGTB = OpRelat("GT", Var("a"), Var("b")) + val exprBMinusA = OpArith("Minus",Var("b"), Var("a")) + val exprAMinusB = OpArith("Minus",Var("a"), Var("b")) + + val program = + WhileProgram( + IfThenElse( + Condition(exprAGTB,1), + Sequence(Assignment("x", exprBMinusA, 2), Assignment("y", exprAMinusB, 3)), + Sequence(Assignment("y", exprBMinusA,4), Assignment("x", exprAMinusB, 5)) + ) + ) + + + val resultCFG = CFGBuilder.buildR(program) + + val expectedCFG = + Set( + (5, 4) + ,(3, 2) + ,(2, 1) + ,(4, 1) + ) + + assert(expectedCFG == resultCFG) + } + } From ffdb7ed53bc77a023350eb23ac4476ab9d4dc623 Mon Sep 17 00:00:00 2001 From: Matheus Bernardo Date: Tue, 31 Aug 2021 10:07:58 -0300 Subject: [PATCH 4/4] fix: bottom for processing --- src/main/scala/br/unb/cic/wlang/AvailableExpression.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala b/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala index 1d5fc3a..57c5b15 100644 --- a/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala +++ b/src/main/scala/br/unb/cic/wlang/AvailableExpression.scala @@ -73,7 +73,7 @@ object AvailableExpression { (from, to) <- flow(program.stmt) if to == label } yield abstraction.exit(from)).toList - sets.foldLeft[Set[AExp]](sets.head)((s1,s2) => s1 intersect s2) + sets.foldLeft[Set[AExp]](nonTrivialExpressions(program))((s1,s2) => s1 intersect s2) } }