Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Scala Macro Tools
提交
99db9fc4
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 搜索 >>
提交
99db9fc4
编写于
6月 29, 2021
作者:
梦境迷离
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add `@log` and refactor code
上级
e1f5fd02
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
264 addition
and
50 deletion
+264
-50
examples213/src/main/scala/io/github/dreamylost/LogMain.scala
...ples213/src/main/scala/io/github/dreamylost/LogMain.scala
+15
-0
src/main/scala/io/github/dreamylost/MacroCommon.scala
src/main/scala/io/github/dreamylost/MacroCommon.scala
+75
-0
src/main/scala/io/github/dreamylost/builder.scala
src/main/scala/io/github/dreamylost/builder.scala
+5
-13
src/main/scala/io/github/dreamylost/json.scala
src/main/scala/io/github/dreamylost/json.scala
+7
-8
src/main/scala/io/github/dreamylost/log.scala
src/main/scala/io/github/dreamylost/log.scala
+107
-0
src/main/scala/io/github/dreamylost/synchronized.scala
src/main/scala/io/github/dreamylost/synchronized.scala
+2
-7
src/main/scala/io/github/dreamylost/toString.scala
src/main/scala/io/github/dreamylost/toString.scala
+5
-22
src/test/scala/io/github/dreamylost/LogTest.scala
src/test/scala/io/github/dreamylost/LogTest.scala
+48
-0
未找到文件。
examples213/src/main/scala/io/github/dreamylost/LogMain.scala
0 → 100644
浏览文件 @
99db9fc4
package
io.github.dreamylost
/**
*
* @author 梦境迷离
* @since 2021/6/29
* @version 1.0
*/
object
LogMain
extends
App
{
private
val
log
:
java.util.logging.Logger
=
java
.
util
.
logging
.
Logger
.
getLogger
(
LogMain
.
getClass
.
getName
)
println
(
LogMain
.
getClass
.
getName
)
println
(
log
)
}
src/main/scala/io/github/dreamylost/MacroCommon.scala
0 → 100644
浏览文件 @
99db9fc4
package
io.github.dreamylost
import
scala.reflect.macros.whitebox
/**
* Common methods
*
* @author 梦境迷离
* @since 2021/6/28
* @version 1.0
*/
trait
MacroCommon
{
def
printTree
(
c
:
whitebox.Context
)(
force
:
Boolean
,
resTree
:
c.Tree
)
:
Unit
=
{
c
.
info
(
c
.
enclosingPosition
,
"\n###### Expanded macro ######\n"
+
resTree
.
toString
()
+
"\n###### Expanded macro ######\n"
,
force
=
force
)
}
/**
* Check the class and its accompanying objects, and return the class definition.
*
* @param c
* @param annottees
* @return Return ClassDef
*/
def
checkAndReturnClass
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.universe.ClassDef
=
{
import
c.universe._
val
annotateeClass
:
ClassDef
=
annottees
.
map
(
_
.
tree
).
toList
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
classDecl
case
(
classDecl
:
ClassDef
)
::
(
compDecl
:
ModuleDef
)
::
Nil
=>
classDecl
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Unexpected annottee. Only applicable to class definitions."
)
}
annotateeClass
}
/**
* Modify the associated object itself according to whether there is an associated object.
*
* @param c
* @param annottees
* @param modifyAction The dependent type need aux-pattern in scala2. Now let's get around this.
* @return Return the result of modifyAction
*/
def
handleWithImplType
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
(
modifyAction
:
(
c.universe.ClassDef
,
Option
[
c.universe.ModuleDef
])
=>
Any
)
:
c.Expr
[
Nothing
]
=
{
import
c.universe._
annottees
.
map
(
_
.
tree
)
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
modifyAction
(
classDecl
,
None
).
asInstanceOf
[
c.Expr
[
Nothing
]]
case
(
classDecl
:
ClassDef
)
::
(
compDecl
:
ModuleDef
)
::
Nil
=>
modifyAction
(
classDecl
,
Some
(
compDecl
)).
asInstanceOf
[
c.Expr
[
Nothing
]]
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Invalid annottee"
)
}
}
/**
* Expand the class and check whether the class is a case class.
*
* @param c
* @param annotateeClass classDef
* @return Return true if it is a case class
*/
def
isCaseClass
(
c
:
whitebox.Context
)(
annotateeClass
:
c.universe.ClassDef
)
:
Boolean
=
{
import
c.universe._
annotateeClass
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
if
(
mods
.
asInstanceOf
[
Modifiers
].
hasFlag
(
Flag
.
CASE
))
{
c
.
warning
(
c
.
enclosingPosition
,
"'toString' annotation is used on 'case class'."
)
true
}
else
false
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
s
"Annotation is only supported on class. classDef: $annotateeClass"
)
}
}
}
src/main/scala/io/github/dreamylost/builder.scala
浏览文件 @
99db9fc4
...
...
@@ -16,7 +16,7 @@ final class builder extends StaticAnnotation {
}
object
builderMacro
{
object
builderMacro
extends
MacroCommon
{
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
...
...
@@ -98,7 +98,8 @@ object builderMacro {
}
}
def
modifiedDeclaration
(
classDecl
:
ClassDef
,
compDeclOpt
:
Option
[
ModuleDef
]
=
None
)
:
c.Expr
[
Nothing
]
=
{
// The dependent type need aux-pattern in scala2. Now let's get around this.
def
modifiedDeclaration
(
classDecl
:
ClassDef
,
compDeclOpt
:
Option
[
ModuleDef
]
=
None
)
:
Any
=
{
val
(
mods
,
className
,
fields
)
=
classDecl
match
{
case
q
"$mods class $className(..$fields) extends ..$bases { ..$body }"
=>
c
.
info
(
c
.
enclosingPosition
,
s
"modifiedDeclaration className: $className, fields: $fields"
,
force
=
true
)
...
...
@@ -123,18 +124,9 @@ object builderMacro {
c
.
info
(
c
.
enclosingPosition
,
s
"builder annottees: $annottees"
,
true
)
val
resTree
=
annottees
.
map
(
_
.
tree
)
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
modifiedDeclaration
(
classDecl
)
case
(
classDecl
:
ClassDef
)
::
(
compDecl
:
ModuleDef
)
::
Nil
=>
modifiedDeclaration
(
classDecl
,
Some
(
compDecl
))
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Invalid annottee"
)
}
val
resTree
=
handleWithImplType
(
c
)(
annottees
:
_
*
)(
modifiedDeclaration
)
printTree
(
c
)(
force
=
true
,
resTree
.
tree
)
// Print the ast
c
.
info
(
c
.
enclosingPosition
,
"\n###### Expanded macro ######\n"
+
resTree
.
toString
()
+
"\n###### Expanded macro ######\n"
,
force
=
true
)
resTree
}
}
src/main/scala/io/github/dreamylost/json.scala
浏览文件 @
99db9fc4
...
...
@@ -16,7 +16,7 @@ final class json extends StaticAnnotation {
def
macroTransform
(
annottees
:
Any*
)
:
Any
=
macro
jsonMacro
.
impl
}
object
jsonMacro
{
object
jsonMacro
extends
MacroCommon
{
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
...
...
@@ -50,7 +50,8 @@ object jsonMacro {
}
}
def
modifiedDeclaration
(
classDecl
:
ClassDef
,
compDeclOpt
:
Option
[
ModuleDef
]
=
None
)
:
c.Expr
[
Nothing
]
=
{
// The dependent type need aux-pattern in scala2. Now let's get around this.
def
modifiedDeclaration
(
classDecl
:
ClassDef
,
compDeclOpt
:
Option
[
ModuleDef
]
=
None
)
:
Any
=
{
val
(
className
,
fields
)
=
classDecl
match
{
case
q
"$mods class $className(..$fields) extends ..$bases { ..$body }"
=>
if
(!
mods
.
asInstanceOf
[
Modifiers
].
hasFlag
(
Flag
.
CASE
))
{
...
...
@@ -77,12 +78,10 @@ object jsonMacro {
}
c
.
info
(
c
.
enclosingPosition
,
s
"json annottees: $annottees"
,
true
)
c
.
info
(
c
.
enclosingPosition
,
s
"json annottees: $annottees"
,
force
=
true
)
val
resTree
=
handleWithImplType
(
c
)(
annottees
:
_
*
)(
modifiedDeclaration
)
printTree
(
c
)(
force
=
true
,
resTree
.
tree
)
annottees
.
map
(
_
.
tree
)
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
modifiedDeclaration
(
classDecl
)
case
(
classDecl
:
ClassDef
)
::
(
compDecl
:
ModuleDef
)
::
Nil
=>
modifiedDeclaration
(
classDecl
,
Some
(
compDecl
))
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Invalid annottee"
)
}
resTree
}
}
src/main/scala/io/github/dreamylost/log.scala
0 → 100644
浏览文件 @
99db9fc4
package
io.github.dreamylost
import
io.github.dreamylost.LogType.LogType
import
scala.annotation.
{
StaticAnnotation
,
compileTimeOnly
}
import
scala.language.experimental.macros
import
scala.reflect.macros.whitebox
/**
*
* @author 梦境迷离
* @param verbose Whether to enable detailed log.
* @param logType Specifies the type of `log` that needs to be generated
* @since 2021/6/28
* @version 1.0
*/
@compileTimeOnly
(
"enable macro to expand macro annotations"
)
final
class
log
(
verbose
:
Boolean
=
false
,
logType
:
LogType.LogType
=
LogType
.
JLog
)
extends
StaticAnnotation
{
def
macroTransform
(
annottees
:
Any*
)
:
Any
=
macro
logMacro
.
impl
}
sealed
trait
BaseLog
{
val
typ
:
LogType
def
getTemplate
(
c
:
whitebox.Context
)(
t
:
String
,
isClass
:
Boolean
)
:
c.Tree
}
object
LogType
extends
Enumeration
{
type
LogType
=
Value
val
JLog
,
Log4j2
,
Slf4j
=
Value
private
lazy
val
types
=
Map
(
JLog
->
JBaseLogImpl
,
Log4j2
->
Log4J2Impl
,
Slf4j
->
Slf4jImpl
)
def
getLogImpl
(
logType
:
LogType
)
:
BaseLog
=
{
types
.
getOrElse
(
logType
,
default
=
throw
new
Exception
(
s
"Not support log: $logType"
))
}
}
object
JBaseLogImpl
extends
BaseLog
{
override
val
typ
:
LogType
=
LogType
.
JLog
override
def
getTemplate
(
c
:
whitebox.Context
)(
t
:
String
,
isClass
:
Boolean
)
:
c.Tree
=
{
import
c.universe._
if
(
isClass
)
{
q
"""private val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(classOf[${TypeName(t)}].getName)"""
}
else
{
q
"""private val log: java.util.logging.Logger = java.util.logging.Logger.getLogger(${TermName(t)}.getClass.getName)"""
}
}
}
object
Log4J2Impl
extends
BaseLog
{
override
val
typ
:
LogType
=
LogType
.
Log4j2
override
def
getTemplate
(
c
:
whitebox.Context
)(
t
:
String
,
isClass
:
Boolean
)
:
c.Tree
=
???
}
object
Slf4jImpl
extends
BaseLog
{
override
val
typ
:
LogType
=
LogType
.
Slf4j
override
def
getTemplate
(
c
:
whitebox.Context
)(
t
:
String
,
isClass
:
Boolean
)
:
c.Tree
=
???
}
object
logMacro
extends
MacroCommon
{
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
val
args
:
(
Boolean
,
LogType
)
=
c
.
prefix
.
tree
match
{
case
q
"new log(logType=$logType)"
=>
(
false
,
c
.
eval
[
LogType
](
c
.
Expr
(
logType
)))
case
q
"new log(verbose=$verbose)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
verbose
)),
LogType
.
JLog
)
case
q
"new log(verbose=$verbose, logType=$logType)"
=>
(
c
.
eval
[
Boolean
](
c
.
Expr
(
verbose
)),
c
.
eval
[
LogType
](
c
.
Expr
(
logType
)))
case
q
"new log()"
=>
(
false
,
LogType
.
JLog
)
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"unexpected annotation pattern!"
)
}
c
.
info
(
c
.
enclosingPosition
,
s
"annottees: $annottees, args: $args"
,
force
=
args
.
_1
)
val
logTree
=
annottees
.
map
(
_
.
tree
)
match
{
// Match a class, and expand, get class/object name.
case
(
classDef
@
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
)
::
_
=>
LogType
.
getLogImpl
(
args
.
_2
).
getTemplate
(
c
)(
tpname
.
asInstanceOf
[
TypeName
].
toTermName
.
decodedName
.
toString
,
isClass
=
true
)
case
(
classDef
@
q
"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
)
::
_
=>
LogType
.
getLogImpl
(
args
.
_2
).
getTemplate
(
c
)(
tpname
.
asInstanceOf
[
TermName
].
decodedName
.
toString
,
isClass
=
false
)
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
s
"Annotation is only supported on class or object."
)
}
// add result into class
val
resTree
=
annottees
.
map
(
_
.
tree
)
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
::
_
=>
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..${List(logTree) ::: stats.toList} }"
case
q
"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
::
_
=>
q
"$mods object $tpname extends { ..$earlydefns } with ..$parents { $self => ..${List(logTree) ::: stats.toList} }"
}
printTree
(
c
)(
force
=
args
.
_1
,
resTree
)
c
.
Expr
[
Any
](
resTree
)
}
}
src/main/scala/io/github/dreamylost/synchronized.scala
浏览文件 @
99db9fc4
...
...
@@ -20,7 +20,7 @@ final class synchronized(
def
macroTransform
(
annottees
:
Any*
)
:
Any
=
macro
synchronizedMacro
.
impl
}
object
synchronizedMacro
{
object
synchronizedMacro
extends
MacroCommon
{
def
impl
(
c
:
whitebox.Context
)(
annottees
:
c.Expr
[
Any
]*)
:
c.Expr
[
Any
]
=
{
import
c.universe._
...
...
@@ -47,12 +47,7 @@ object synchronizedMacro {
}
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Invalid annotation target: not a method"
)
}
// Print the ast
c
.
info
(
c
.
enclosingPosition
,
"\n###### Expanded macro ######\n"
+
resTree
.
toString
()
+
"\n###### Expanded macro ######\n"
,
force
=
args
.
_1
)
printTree
(
c
)(
args
.
_1
,
resTree
)
c
.
Expr
[
Any
](
resTree
)
}
}
src/main/scala/io/github/dreamylost/toString.scala
浏览文件 @
99db9fc4
...
...
@@ -29,7 +29,7 @@ final class toString(
final
case
class
Argument
(
verbose
:
Boolean
,
includeInternalFields
:
Boolean
,
includeFieldNames
:
Boolean
,
callSuper
:
Boolean
)
object
stringMacro
{
object
stringMacro
extends
MacroCommon
{
def
printField
(
c
:
whitebox.Context
)(
argument
:
Argument
,
lastParam
:
Option
[
String
],
field
:
c.universe.Tree
)
:
c.universe.Tree
=
{
import
c.universe._
...
...
@@ -71,7 +71,7 @@ object stringMacro {
case
_:
ValDef
=>
true
case
mem
:
MemberDef
=>
c
.
info
(
c
.
enclosingPosition
,
s
"MemberDef: ${mem.toString}"
,
force
=
argument
.
verbose
)
if
(
mem
.
toString
().
startsWith
(
"override def toString"
))
{
if
(
mem
.
toString
().
startsWith
(
"override def toString"
))
{
// TODO better way
c
.
abort
(
mem
.
pos
,
"'toString' method has already defined, please remove it or not use'@toString'"
)
}
false
...
...
@@ -131,20 +131,8 @@ object stringMacro {
val
argument
=
Argument
(
arg
.
_1
,
arg
.
_2
,
arg
.
_3
,
arg
.
_4
)
c
.
info
(
c
.
enclosingPosition
,
s
"toString annottees: $annottees"
,
force
=
argument
.
verbose
)
// Check the type of the class, which can only be defined on the ordinary class
val
annotateeClass
:
ClassDef
=
annottees
.
map
(
_
.
tree
).
toList
match
{
case
(
classDecl
:
ClassDef
)
::
Nil
=>
classDecl
case
(
classDecl
:
ClassDef
)
::
(
compDecl
:
ModuleDef
)
::
Nil
=>
classDecl
case
_
=>
c
.
abort
(
c
.
enclosingPosition
,
"Unexpected annottee. Only applicable to class definitions."
)
}
val
isCase
:
Boolean
=
{
annotateeClass
match
{
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
if
(
mods
.
asInstanceOf
[
Modifiers
].
hasFlag
(
Flag
.
CASE
))
{
c
.
warning
(
c
.
enclosingPosition
,
"'toString' annotation is used on 'case class'."
)
true
}
else
false
}
}
val
annotateeClass
:
ClassDef
=
checkAndReturnClass
(
c
)(
annottees
:
_
*
)
val
isCase
:
Boolean
=
isCaseClass
(
c
)(
annotateeClass
)
c
.
info
(
c
.
enclosingPosition
,
s
"impl argument: $argument, isCase: $isCase"
,
force
=
argument
.
verbose
)
val
resMethod
=
toStringTemplateImpl
(
c
)(
argument
,
annotateeClass
)
...
...
@@ -152,12 +140,7 @@ object stringMacro {
case
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
=>
q
"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..${stats.toList.:+(resMethod)} }"
}
// Print the ast
c
.
info
(
c
.
enclosingPosition
,
"\n###### Expanded macro ######\n"
+
resTree
.
toString
()
+
"\n###### Expanded macro ######\n"
,
force
=
argument
.
verbose
)
printTree
(
c
)(
argument
.
verbose
,
resTree
)
c
.
Expr
[
Any
](
resTree
)
}
}
src/test/scala/io/github/dreamylost/LogTest.scala
0 → 100644
浏览文件 @
99db9fc4
package
io.github.dreamylost
import
org.scalatest.
{
FlatSpec
,
Matchers
}
/**
*
* @author 梦境迷离
* @since 2021/6/28
* @version 1.0
*/
class
LogTest
extends
FlatSpec
with
Matchers
{
"log1"
should
"ok on class"
in
{
"""@log(verbose=true) class TestClass1(val i: Int = 0, var j: Int) {
log.info("hello")
}"""
should
compile
"""@log class TestClass2(val i: Int = 0, var j: Int)"""
should
compile
"""@log() class TestClass3(val i: Int = 0, var j: Int)"""
should
compile
"""@log(verbose=true) class TestClass4(val i: Int = 0, var j: Int)"""
should
compile
"""@log(logType=io.github.dreamylost.LogType.JLog) class TestClass5(val i: Int = 0, var j: Int)"""
should
compile
"""@log(verbose=true, logType=io.github.dreamylost.LogType.JLog) class TestClass6(val i: Int = 0, var j: Int)"""
should
compile
}
"log2"
should
"ok on case class"
in
{
"""@log(verbose=true) case class TestClass1(val i: Int = 0, var j: Int) {
log.info("hello")
}"""
should
compile
"""@log case class TestClass2(val i: Int = 0, var j: Int)"""
should
compile
"""@log() case class TestClass3(val i: Int = 0, var j: Int)"""
should
compile
"""@log(verbose=true) case class TestClass4(val i: Int = 0, var j: Int)"""
should
compile
"""@log(logType=io.github.dreamylost.LogType.JLog) case class TestClass5(val i: Int = 0, var j: Int)"""
should
compile
"""@log(verbose=true, logType=io.github.dreamylost.LogType.JLog) case class TestClass6(val i: Int = 0, var j: Int)"""
should
compile
}
"log3"
should
"ok on object"
in
{
"""@log(verbose=true) object TestClass1 {
log.info("hello")
}"""
should
compile
"""@log object TestClass2"""
should
compile
"""@log() object TestClass3"""
should
compile
"""@log(verbose=true) object TestClass4"""
should
compile
"""@log(logType=io.github.dreamylost.LogType.JLog) object TestClass5"""
should
compile
"""@log(verbose=true, logType=io.github.dreamylost.LogType.JLog) object TestClass6"""
should
compile
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录