Last active
October 17, 2017 10:54
-
-
Save mandubian/03cec82b6ab4433d8989dc37e550d9a7 to your computer and use it in GitHub Desktop.
Scalameta AST diffing enriched by Semantic Database
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create ScalaMeta Semantic DB from previous 2 files | |
val sdb = SemanticdbIndex.load(Sourcepath(BuildInfo.sourcepath), Classpath(BuildInfo.classpath)) | |
// Print SDB info (always interesting) | |
println(sdb.database) | |
// Retrieve source document | |
val src = sdb.documents(0) | |
// Build source tree (for now, there is no API to do that more straightforward | |
val srcTree = src.input.text.parse[Source].get.children.head.children.tail.map(TreeNode(_)) | |
println(s"srcTree:\n${srcTree}") | |
/* | |
srcTree: | |
List(TreeNode(object Test { | |
val a = 3 | |
case class Foo(a: String) | |
def f(foo: Foo): String = foo.a | |
})) | |
*/ | |
// Build target document | |
val target = sdb.documents(1) | |
// Build target tree (for now, there is no API to do that more straightforward | |
val targetTree = target.input.text.parse[Source].get.children.head.children.tail.map(TreeNode(_)) | |
println(s"target:\n${targetTree}") | |
/* | |
target: | |
List(TreeNode(object Test { | |
val b = 2 | |
case class Bar(a: Int) | |
def f(foo: Bar): Int = foo.a | |
})) | |
*/ | |
// Compute Diff of both trees | |
val diff = Diff(srcTree, targetTree) | |
println(s"diff:\n${diff}") | |
/* | |
diff: | |
Cpy(ObjectType(Defn.Object),Cpy(TreeListFieldLabel(mods,0),Cpy(SimpleFieldLabel(name),Cpy(ValueLabel(TreeNode(Test)),Cpy(SimpleFieldLabel(templ),Cpy(ObjectType(Template),Cpy(TreeListFieldLabel(early,0),Cpy(TreeListFieldLabel(inits,0),Cpy(SimpleFieldLabel(self),Cpy(ValueLabel(TreeNode()),Cpy(TreeListFieldLabel(stats,3),Cpy(ObjectType(Defn.Val),Cpy(TreeListFieldLabel(mods,0),Cpy(TreeListFieldLabel(pats,1),Cpy(ObjectType(Pat.Var),Cpy(SimpleFieldLabel(name),Del(ValueLabel(TreeNode(a)),Ins(ValueLabel(TreeNode(b)),Cpy(TreeOptionFieldLabel(decltpe,false),Cpy(SimpleFieldLabel(rhs),Del(ValueLabel(TreeNode(3)),Ins(ValueLabel(TreeNode(2)),Cpy(ObjectType(Defn.Class),Cpy(TreeListFieldLabel(mods,1),Cpy(ValueLabel(TreeNode(case)),Cpy(SimpleFieldLabel(name),Del(ValueLabel(TreeNode(Foo)),Ins(ValueLabel(TreeNode(Bar)),Cpy(TreeListFieldLabel(tparams,0),Cpy(SimpleFieldLabel(ctor),Cpy(ObjectType(Ctor.Primary),Cpy(TreeListFieldLabel(mods,0),Cpy(SimpleFieldLabel(name),Cpy(ValueLabel(TreeNode()),Cpy(TreeListListFieldLabel(paramss,1),Cpy(TreeListFieldLabel(paramss,1),Cpy(ObjectType(Term.Param),Cpy(TreeListFieldLabel(mods,0),Cpy(SimpleFieldLabel(name),Cpy(ValueLabel(TreeNode(a)),Cpy(TreeOptionFieldLabel(decltpe,true),Del(ValueLabel(TreeNode(String)),Ins(ValueLabel(TreeNode(Int)),Cpy(TreeOptionFieldLabel(default,false),Cpy(SimpleFieldLabel(templ),Cpy(ValueLabel(TreeNode()),Cpy(ObjectType(Defn.Def),Cpy(TreeListFieldLabel(mods,0),Cpy(SimpleFieldLabel(name),Cpy(ValueLabel(TreeNode(f)),Cpy(TreeListFieldLabel(tparams,0),Cpy(TreeListListFieldLabel(paramss,1),Cpy(TreeListFieldLabel(paramss,1),Cpy(ObjectType(Term.Param),Cpy(TreeListFieldLabel(mods,0),Cpy(SimpleFieldLabel(name),Cpy(ValueLabel(TreeNode(foo)),Cpy(TreeOptionFieldLabel(decltpe,true),Del(ValueLabel(TreeNode(Foo)),Ins(ValueLabel(TreeNode(Bar)),Cpy(TreeOptionFieldLabel(default,false),Cpy(TreeOptionFieldLabel(decltpe,true),Del(ValueLabel(TreeNode(String)),Ins(ValueLabel(TreeNode(Int)),Cpy(SimpleFieldLabel(body),Cpy(ValueLabel(TreeNode(foo.a)),End)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) | |
*/ | |
// Test patching source using diff | |
val patched = Diff.patch(diff, srcTree) | |
println(s"patched:\n${patched}") | |
/* | |
patched: | |
Right(List(TreeNode(object Test { | |
val b = 2 | |
case class Bar(a: Int) | |
def f(foo: Bar): Int = foo.a | |
}))) | |
*/ | |
// Map Semantic-Diff tree symbols into their semantic counterparts | |
val sem0 = toSemantic(sdb, src.input, target.input, diff, fullDetailsFrom = false, fullDetailsTo = false) | |
// Extracts Source from Semantic-Diff | |
val Right(List(TreeNode(resrc))) = Diff.extractSource(sem0, List()) | |
println(s"resrc:\n${resrc}") | |
/* | |
resrc: | |
object Test { | |
val _root_.toto.Test.a = 3 | |
case class _root_.toto.Test.Foo(a: _root_.scala.Predef.String) | |
def f(foo: _root_.toto.Test.Foo): _root_.scala.Predef.String = foo.a | |
} | |
*/ | |
// Extracts Target from Semantic-Diff | |
val Right(List(TreeNode(retarget))) = Diff.extractTarget(sem0, List()) | |
println(s"retarget:\n${retarget}") | |
/* | |
retarget: | |
object Test { | |
val _root_.tata.Test.b = 2 | |
case class _root_.tata.Test.Bar(a: _root_.scala.Int) | |
def f(foo: _root_.tata.Test.Bar): _root_.scala.Int = foo.a | |
} | |
*/ | |
// Map Semantic-Diff tree symbols into their semantic counterparts with all denotation details | |
val sem = toSemantic(sdb, src.input, target.input, diff, fullDetailsFrom = true, fullDetailsTo = false) | |
// Visualize a syntax of Semantic Diff using semantic denotation info | |
val diffSyntax = DiffSyntax(scala.meta.dialects.Scala212)(sem) | |
println(s"diffSyntax:\n${diffSyntax}") | |
/* | |
diffSyntax: | |
*/ | |
object Test { | |
val /*FROM `val _root_.toto.Test.a: Int` TO*/ _root_.tata.Test.b = /*FROM 3 TO*/ 2 | |
case class /*FROM `case class _root_.toto.Test.Foo` TO*/ _root_.tata.Test.Bar(a: /*FROM `type _root_.scala.Predef.String: String` TO*/ _root_.scala.Int) | |
def f(foo: /*FROM `case class _root_.toto.Test.Foo` TO*/ _root_.tata.Test.Bar): /*FROM `type _root_.scala.Predef.String: String` TO*/ _root_.scala.Int = foo.a | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package toto | |
object Test { | |
val a = 3 | |
case class Foo(a: String) | |
def f(foo: Foo): String = foo.a | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tata | |
object Test { | |
val b = 2 | |
case class Bar(a: Int) | |
def f(foo: Bar): Int = foo.a | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment