Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Scala Macro Tools
提交
042a677a
S
Scala Macro Tools
项目概览
梦境迷离
/
Scala Macro Tools
上一次同步 大约 1 年
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Scala Macro Tools
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
042a677a
编写于
6月 15, 2021
作者:
梦境迷离
提交者:
GitHub
6月 15, 2021
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #2 from jxnu-liguobin/dev
release
上级
4502f8bd
be066461
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
149 addition
and
75 deletion
+149
-75
README.md
README.md
+8
-30
Version.sbt
Version.sbt
+1
-1
build.sbt
build.sbt
+21
-4
project/Publishing.scala
project/Publishing.scala
+44
-0
src/main/scala/io/github/dreamylost/toString.scala
src/main/scala/io/github/dreamylost/toString.scala
+22
-23
src/test/scala/io/github/dreamylost/ToStringTest.scala
src/test/scala/io/github/dreamylost/ToStringTest.scala
+53
-17
未找到文件。
README.md
浏览文件 @
042a677a
...
...
@@ -7,9 +7,10 @@ scala macro and abstract syntax tree learning code.
-
Argument
-
`verbose`
Whether to enable detailed log.
-
`withFieldName`
Whether to include the name of the field in the toString.
-
`containsCtorParams`
Whether to include the fields of the primary constructor.
-
`withInternalField`
Whether to include the fields defined within a class.
-
Support
`case class`
and
`class`
.
-
source code1
-
Example
```
scala
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
...
...
@@ -18,33 +19,10 @@ class TestClass(val i: Int = 0, var j: Int) {
var
x
:
String
=
"world"
}
case
class
TestClass2
(
i
:
Int
=
0
,
var
j
:
Int
)
// No method body, only have primary constructor.
println
(
new
TestClass
(
1
,
2
));
```
-
when withFieldName=false containsCtorParams=false
```
println(new TestClass(1, 2))
TestClass(0, hello, world)
```
-
when withFieldName=false containsCtorParams=true
```
println(new TestClass(1, 2))
TestClass(1, 2, 0, hello, world)
```
-
when withFieldName=true containsCtorParams=false
```
println(new TestClass(1, 2))
TestClass(y=0, z=hello, x=world)
```
-
when withFieldName=true containsCtorParams=true
```
println(new TestClass(1, 2))
TestClass(i=1, j=2, y=0, z=hello, x=world)
```
\ No newline at end of file
| withInternalField / withFieldName | false |true
| ---- | ---- |----|
|false|
```TestClass(1, 2)```
|
```TestClass(i=0, j=2)```
|
|true|
```TestClass(1, 2, 0, hello, world)```
|
```TestClass(i=1, j=2, y=0, z=hello, x=world)```
|
\ No newline at end of file
Version.sbt
浏览文件 @
042a677a
version
in
Scope
.
ThisScope
:=
"0.1
-SNAPSHOT"
version
in
ThisBuild
:=
"0.0.2
-SNAPSHOT"
build.sbt
浏览文件 @
042a677a
import
sbtrelease.ReleaseStateTransformations._
name
:=
"scala-macro-tools"
scalaVersion
:=
"2.13.6"
organization
:=
"io.github.jxnu-liguobin"
...
...
@@ -7,7 +9,7 @@ lazy val scala211 = "2.11.12"
lazy
val
scala213
=
"2.13.6"
lazy
val
supportedScalaVersions
=
List
(
scala213
,
scala212
,
scala211
)
lazy
val
core
=
(
project
in
file
(
"."
))
lazy
val
root
=
(
project
in
file
(
"."
))
.
settings
(
crossScalaVersions
:=
supportedScalaVersions
,
libraryDependencies
++=
Seq
(
...
...
@@ -17,7 +19,22 @@ lazy val core = (project in file("."))
),
Compile
/
scalacOptions
++=
{
CrossVersion
.
partialVersion
(
scalaVersion
.
value
)
match
{
case
Some
((
2
,
n
))
if
n
<=
12
=>
Nil
case
_
=>
List
(
"-Ymacro-annotations"
,
"-Ymacro-debug-verbose"
)
case
_
=>
List
(
"-Ymacro-annotations"
/*, "-Ymacro-debug-verbose"*/
)
}
}
)
},
releaseIgnoreUntrackedFiles
:=
true
,
releaseCrossBuild
:=
true
,
releaseProcess
:=
Seq
[
ReleaseStep
](
checkSnapshotDependencies
,
inquireVersions
,
runClean
,
releaseStepCommandAndRemaining
(
"^ compile"
),
setReleaseVersion
,
commitReleaseVersion
,
tagRelease
,
releaseStepCommandAndRemaining
(
"^ publishSigned"
),
setNextVersion
,
commitNextVersion
,
pushChanges
)
).
settings
(
Publishing
.
publishSettings
)
project/Publishing.scala
0 → 100644
浏览文件 @
042a677a
import
sbt.Keys._
import
sbt._
import
xerial.sbt.Sonatype.autoImport.sonatypeProfileName
/**
* sbt publish setting
*
* @author 梦境迷离 dreamylost
* @since 2020-07-19
* @version v1.0
*/
object
Publishing
{
//publish by sbt publishSigned
lazy
val
publishSettings
=
Seq
(
credentials
+=
Credentials
(
Path
.
userHome
/
".ivy2"
/
".sonatype_credentials"
),
publishTo
:=
{
val
nexus
=
"https://oss.sonatype.org/"
if
(
isSnapshot
.
value
)
Some
(
"snapshots"
at
nexus
+
"content/repositories/snapshots"
)
else
Some
(
"releases"
at
nexus
+
"service/local/staging/deploy/maven2"
)
},
licenses
:=
Seq
(
"MIT"
->
url
(
"https://opensource.org/licenses/MIT"
)),
publishMavenStyle
:=
true
,
publishArtifact
in
Test
:=
false
,
pomIncludeRepository
:=
{
_
=>
false
},
developers
:=
List
(
Developer
(
id
=
"dreamylost"
,
name
=
"梦境迷离"
,
email
=
"dreamylost@outlook.com"
,
url
=
url
(
"https://dreamylost.cn"
)
)),
sonatypeProfileName
:=
organization
.
value
,
isSnapshot
:=
version
.
value
endsWith
"SNAPSHOT"
,
homepage
:=
Some
(
url
(
"https://github.com/jxnu-liguobin"
)),
scmInfo
:=
Some
(
ScmInfo
(
url
(
"https://github.com/bitlap/scala-macro-tools"
),
"scm:git@github.com:bitlap/scala-macro-tools.git"
))
)
}
src/main/scala/io/github/
liguobin
/toString.scala
→
src/main/scala/io/github/
dreamylost
/toString.scala
浏览文件 @
042a677a
package
io.github.
liguobin
package
io.github.
dreamylost
import
scala.annotation.
{
StaticAnnotation
,
compileTimeOnly
}
import
scala.annotation.
{
StaticAnnotation
,
compileTimeOnly
}
import
scala.language.experimental.macros
import
scala.reflect.macros.whitebox
...
...
@@ -9,23 +9,23 @@ import scala.reflect.macros.whitebox
*
* @author 梦境迷离
* @param verbose Whether to enable detailed log.
* @param
containsCtorParams Whether to include the fields of the primary constructor
.
* @param
withInternalField Whether to include the fields defined within a class
.
* @param withFieldName Whether to include the name of the field in the toString.
* @since 2021/6/13
* @version 1.0
*/
@compileTimeOnly
(
"enable macro to expand macro annotations"
)
class
toString
(
verbose
:
Boolean
=
false
,
containsCtorParams
:
Boolean
=
true
,
withFieldName
:
Boolean
=
true
)
extends
StaticAnnotation
{
final
class
toString
(
verbose
:
Boolean
=
false
,
withInternalField
:
Boolean
=
true
,
withFieldName
:
Boolean
=
true
)
extends
StaticAnnotation
{
def
macroTransform
(
annottees
:
Any*
)
:
Any
=
macro
stringMacro
.
impl
}
case
class
Argument
(
verbose
:
Boolean
,
containsCtorParams
:
Boolean
,
withFieldName
:
Boolean
)
case
class
Argument
(
verbose
:
Boolean
,
withInternalField
:
Boolean
,
withFieldName
:
Boolean
)
object
stringMacro
{
...
...
@@ -35,9 +35,9 @@ object stringMacro {
if
(
argument
.
withFieldName
)
{
lastParam
.
fold
(
q
"$field"
)
{
lp
=>
field
match
{
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""${tname.toString()}+${"="}+this.$tname+${", "}"""
else
q
"""${tname.toString()}+${"="}+this.$tname"""
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""${tname.toString()}+${"="}+this.$tname+${", "}"""
else
q
"""${tname.toString()}+${"="}+this.$tname"""
case
_
=>
q
"$field"
}
...
...
@@ -45,9 +45,9 @@ object stringMacro {
}
else
{
lastParam
.
fold
(
q
"$field"
)
{
lp
=>
field
match
{
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""$tname+${", "}"""
else
q
"""$tname"""
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""$tname+${", "}"""
else
q
"""$tname"""
case
_
=>
if
(
field
.
toString
()
!=
lp
)
q
"""$field+${", "}"""
else
q
"""$field"""
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""$tname+${", "}"""
else
q
"""$tname"""
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
if
(
tname
.
toString
()
!=
lp
)
q
"""$tname+${", "}"""
else
q
"""$tname"""
case
_
=>
if
(
field
.
toString
()
!=
lp
)
q
"""$field+${", "}"""
else
q
"""$field"""
}
}
...
...
@@ -72,23 +72,21 @@ object stringMacro {
c
.
abort
(
mem
.
pos
,
"'toString' method has already defined, please remove it or not use'@toString'"
)
}
false
case
m
:
DefDef
=>
false
case
_
=>
false
})
// For the parameters of a given constructor, separate the parameter components and extract the constructor parameters containing val and var
val
ctorParams
=
annotteeClassParams
.
asInstanceOf
[
List
[
List
[
Tree
]]].
flatten
.
map
{
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
tree
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
tree
case
tree
@
q
"$mods val $tname: $tpt = $expr"
=>
tree
case
tree
@
q
"$mods var $tname: $tpt = $expr"
=>
tree
}
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, ctorParams: ${ctorParams.toString()}"
,
force
=
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"className: $className, fields: ${annotteeClassFieldDefinitions.toString()}"
,
force
=
true
)
val
member
=
if
(
argument
.
containsCtorParams
)
ctorParams
++
annotteeClassFieldDefinitions
else
annotteeClassFieldDefinition
s
val
member
=
if
(
argument
.
withInternalField
)
ctorParams
++
annotteeClassFieldDefinitions
else
ctorParam
s
val
lastParam
=
member
.
lastOption
.
map
{
case
v
:
ValDef
=>
v
.
name
.
toTermName
.
decodedName
.
toString
case
c
=>
c
.
toString
case
c
=>
c
.
toString
}
val
paramsWithName
=
member
.
foldLeft
(
q
"${""}"
)((
res
,
acc
)
=>
q
"$res + ${printField(c)(argument, lastParam, acc)}"
)
//scala/bug https://github.com/scala/bug/issues/3967 not be 'Foo(i=1,j=2)' in standard library
...
...
@@ -98,16 +96,17 @@ object stringMacro {
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
// extract parameters of annotation
// extract 'isVerbose' parameters of annotation
val
arg
=
c
.
prefix
.
tree
match
{
case
q
"new toString($aa, $bb, $cc)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
aa
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)))
case
q
"new toString(withInternalField=$bb, withFieldName=$cc)"
=>
(
false
,
c
.
eval
[
Boolean
](
c
.
Expr
(
bb
)),
c
.
eval
[
Boolean
](
c
.
Expr
(
cc
)))
case
q
"new toString()"
=>
(
false
,
true
,
true
)
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"unexpected annotation pattern!"
)
}
val
argument
=
Argument
(
arg
.
_1
,
arg
.
_2
,
arg
.
_3
)
// Check the type of the class, which can only be defined on the ordinary class
val
annotateeClass
:
ClassDef
=
annottees
.
map
(
_
.
tree
).
toList
match
{
case
(
claz
:
ClassDef
)
::
Nil
=>
claz
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Unexpected annottee. Only applicable to class definitions."
)
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Unexpected annottee. Only applicable to class definitions."
)
}
val
isCase
:
Boolean
=
{
annotateeClass
match
{
...
...
@@ -119,7 +118,7 @@ object stringMacro {
}
}
c
.
info
(
c
.
enclosingPosition
,
s
"impl argument: $argument, isCase: $isCase"
,
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"impl argument: $argument, isCase: $isCase"
,
force
=
true
)
val
resMethod
=
toStringTemplateImpl
(
c
)(
argument
,
annotateeClass
)
val
resTree
=
annotateeClass
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
...
...
src/test/scala/io/github/
liguobin
/ToStringTest.scala
→
src/test/scala/io/github/
dreamylost
/ToStringTest.scala
浏览文件 @
042a677a
package
io.github.
liguobin
package
io.github.
dreamylost
import
org.scalatest.
{
FlatSpec
,
Matchers
}
import
org.scalatest.
{
FlatSpec
,
Matchers
}
/**
*
...
...
@@ -10,7 +10,7 @@ import org.scalatest.{FlatSpec, Matchers}
*/
class
ToStringTest
extends
FlatSpec
with
Matchers
{
"toString1"
should
"not contains
constructors parameters
"
in
{
"toString1"
should
"not contains
internal field
"
in
{
@toString
(
false
,
false
,
false
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -19,10 +19,10 @@ class ToStringTest extends FlatSpec with Matchers {
}
val
s
=
new
TestClass
(
1
,
2
).
toString
println
(
s
)
assert
(
s
==
"TestClass(
0, hello, world
)"
)
assert
(
s
==
"TestClass(
1, 2
)"
)
}
"toString2"
should
"contains
constructors parameters
"
in
{
"toString2"
should
"contains
internal field and with name
"
in
{
@toString
(
true
,
true
,
true
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -34,7 +34,7 @@ class ToStringTest extends FlatSpec with Matchers {
assert
(
s
==
"TestClass(i=1, j=2, y=0, z=hello, x=world)"
)
}
"toString3"
should
"not contains
constructors parameters
but with name"
in
{
"toString3"
should
"not contains
internal field
but with name"
in
{
@toString
(
true
,
false
,
true
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -43,10 +43,10 @@ class ToStringTest extends FlatSpec with Matchers {
}
val
s
=
new
TestClass
(
1
,
2
).
toString
println
(
s
)
assert
(
s
==
"TestClass(
y=0, z=hello, x=world
)"
)
assert
(
s
==
"TestClass(
i=1, j=2
)"
)
}
"toString4"
should
"contains
constructors parameters
but without name"
in
{
"toString4"
should
"contains
internal field
but without name"
in
{
@toString
(
true
,
true
,
false
)
class
TestClass
(
val
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -67,10 +67,10 @@ class ToStringTest extends FlatSpec with Matchers {
}
val
s
=
TestClass
(
1
,
2
).
toString
println
(
s
)
assert
(
s
==
"TestClass(
0, hello, world
)"
)
assert
(
s
==
"TestClass(
1, 2
)"
)
}
"toString6"
should
"case class with name"
in
{
"toString6"
should
"case class
not contains internal field and
with name"
in
{
@toString
(
true
,
false
,
true
)
case
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -83,11 +83,11 @@ class ToStringTest extends FlatSpec with Matchers {
println
(
s
)
println
(
s2
)
assert
(
s
==
"TestClass(
y=0, z=hello, x=world
)"
)
assert
(
s
==
"TestClass(
i=1, j=2
)"
)
assert
(
s2
==
"TestClass2(1,2)"
)
}
"toString7"
should
"case class with name"
in
{
"toString7"
should
"case class
contains internal field and
with name"
in
{
@toString
(
true
,
true
,
true
)
case
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -99,7 +99,7 @@ class ToStringTest extends FlatSpec with Matchers {
assert
(
s
==
"TestClass(i=1, j=2, y=0, z=hello, x=world)"
)
}
"toString8"
should
"case class
with name and
itself"
in
{
"toString8"
should
"case class
contains internal field and with name,
itself"
in
{
@toString
(
true
,
true
,
true
)
case
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
,
k
:
TestClass
)
{
val
y
:
Int
=
0
...
...
@@ -111,7 +111,7 @@ class ToStringTest extends FlatSpec with Matchers {
assert
(
s
==
"TestClass(i=1, j=2, k=TestClass(i=1, j=2, k=null, y=0, z=hello, x=world), y=0, z=hello, x=world)"
)
}
"toString9"
should
"case class
with name and
itself2"
in
{
"toString9"
should
"case class
contains internal field and with name,
itself2"
in
{
@toString
(
true
,
true
,
true
)
case
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
)
{
val
y
:
Int
=
0
...
...
@@ -124,7 +124,7 @@ class ToStringTest extends FlatSpec with Matchers {
assert
(
s
==
"TestClass(i=1, j=2, y=0, z=hello, x=world, t=null)"
)
}
"toString10"
should
"case class
with name and
itself3"
in
{
"toString10"
should
"case class
contains internal field with name,
itself3"
in
{
@toString
(
true
,
true
,
true
)
case
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
,
k
:
TestClass
)
{
val
y
:
Int
=
0
...
...
@@ -155,7 +155,7 @@ class ToStringTest extends FlatSpec with Matchers {
class
TestClass
(
i
:
Int
=
0
,
var
j
:
Int
)
val
s
=
new
TestClass
(
1
,
2
).
toString
println
(
s
)
assert
(
s
==
"TestClass()"
)
assert
(
s
==
"TestClass(
i=1, j=2
)"
)
@toString
(
true
,
true
,
true
)
class
TestClass2
(
i
:
Int
=
1
,
var
j
:
Int
=
2
)
...
...
@@ -181,7 +181,7 @@ class ToStringTest extends FlatSpec with Matchers {
case
class
TestClass2
(
i
:
Int
=
1
,
var
j
:
Int
=
3
)
val
s2
=
TestClass2
(
1
,
2
).
toString
println
(
s2
)
assert
(
s2
==
"TestClass2()"
)
assert
(
s2
==
"TestClass2(
1, 2
)"
)
@toString
(
true
,
true
,
true
)
case
class
TestClass3
(
i
:
Int
=
1
,
var
j
:
Int
=
3
)
...
...
@@ -189,4 +189,40 @@ class ToStringTest extends FlatSpec with Matchers {
println
(
s3
)
assert
(
s3
==
"TestClass3(i=1, j=2)"
)
}
"toString14"
should
"empty class and with default params"
in
{
@toString
()
case
class
TestClass1
()
val
s1
=
TestClass1
().
toString
println
(
s1
)
assert
(
s1
==
"TestClass1()"
)
@toString
(
true
,
false
,
false
)
case
class
TestClass2
()
val
s2
=
TestClass2
().
toString
println
(
s2
)
assert
(
s2
==
"TestClass2()"
)
}
"toString15"
should
"super param not find"
in
{
@toString
()
class
TestClass1
(
val
i
:
Int
)
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
case
class
TestClass2
()
extends
TestClass1
(
1
)
val
s1
=
TestClass2
().
toString
println
(
s1
)
assert
(
s1
==
"TestClass2()"
)
//TODO not support println super fields
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
case
class
TestClass3
(
j
:
Int
)
extends
TestClass1
(
j
)
val
s2
=
TestClass3
(
0
).
toString
println
(
s2
)
assert
(
s2
==
"TestClass3(j=0)"
)
@toString
(
withInternalField
=
true
,
withFieldName
=
true
)
class
TestClass4
(
j
:
Int
)
extends
TestClass1
(
j
)
val
s3
=
new
TestClass4
(
0
).
toString
println
(
s3
)
assert
(
s3
==
"TestClass4(j=0)"
)
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录