From c514aaafd6dce38092c51e7f6d3a9a96495768ad Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 22 Nov 2018 15:02:21 +0100 Subject: [PATCH 1/6] Add basic hands on tutorial --- docs/docs/reference/tasty-reflect.md | 193 ++++++++++++++++++ docs/sidebar.yml | 2 + library/src/scala/tasty/reflect/Core.scala | 1 + .../tasty-macro-const/quoted_1.scala | 21 ++ .../tasty-macro-const/quoted_2.scala | 9 + 5 files changed, 226 insertions(+) create mode 100644 docs/docs/reference/tasty-reflect.md create mode 100644 tests/run-separate-compilation/tasty-macro-const/quoted_1.scala create mode 100644 tests/run-separate-compilation/tasty-macro-const/quoted_2.scala diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md new file mode 100644 index 000000000000..849612af321c --- /dev/null +++ b/docs/docs/reference/tasty-reflect.md @@ -0,0 +1,193 @@ +--- +layout: doc-page +title: "TASTy reflect" +--- + +TASTy reflect provides an API that allows inspection and construction of Typed Abstract Syntax Trees (TAST). +It may be used on quoted expressions (`quoted.Expr`) and types (`quoted.Type`) from [Principled Meta-programming](./principled-meta-programming.html) +or on full TASTy files. + +If you are starting using macros see first [Principled Meta-programming](./principled-meta-programming.html) and then follow with API (if really needed). + + +## From quotes and splices to TASTs and back + +`quoted.Expr` and `quoted.Type` are opaque TASTs. +The opaqueness required in [Principled Meta-programming](./principled-meta-programming.html) provide the guarantee that +the generation of code of the macro will be type correct. +Using TASTy reflect will break these guarantees and may fail at macro expansion time, hence additional explicit check must be done. + + +To provide reflection capabilities in macro we need to add an implicit parameter of type `scala.tasty.Reflection` and import it in the scope where it is used. + +```scala +import scala.quoted._ +import scala.tasty._ + +inline def natConst(x: Int): Int = ~natConstImpl('(x)) + +def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { + import reflection._ + ... +} +``` + +`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` with return a `reflection.Term` and `reflection.TypeTree` respectivly. +It will also import all extractors and methods on TASTy reflect trees. For example the `Term.Literal(_)` extractor used bellow. +To easily know which extractor are needed the `reflection.Term.show` method returns the string representation of the extractors. + + +```scala +def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { + import reflection._ + val xTree: Term = x.reflect + xTree match { + case Term.Literal(Constant.Int(n)) => + if (n <= 0) + throw new QuoteError("Parameter must be natural number") + n.toExpr + case _ => + throw new QuoteError("Parameter must be a known constant") + } +} +``` + +The method `reflection.Term.reify[T]` provides a way to to go back to a `quoted.Expr`. +Note that the type must be set explicitly and that if it does not conform to it an exception will be thrown. +In the code above we could have replaced `n.toExpr` by `xTree.reify[Int]`. + +## ASTs of a TASTy file + +To inspect the TASTy trees of a TASTy file a consumer can be defined in the following way. + +```scala +class Consumer extends TastyConsumer { + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + // Do somthing with the tree + } +} +``` + +Then the consumer can be instantiated with the following code to get the tree of the class `foo.Bar` for a foo in the classpath. + +```scala +ConsumeTasty(classpath, List("foo.Bar"), new Consumer) +``` + +## TASTy reflect ASTs + +TASTy reflect provides the following types as defined in `scala.tasty.reflect.Core`. + +```none ++- Tree -+- PackageClause + +- Import + +- Statement -+- Definition --+- PackageDef + | +- ClassDef + | +- TypeDef + | +- DefDef + | +- ValDef + | + +- Term --------+- Ident + +- Select + +- Literal + +- This + +- New + +- NamedArg + +- Apply + +- TypeApply + +- Super + +- Typed + +- Assign + +- Block + +- Lambda + +- If + +- Match + +- Try + +- Return + +- Repeated + +- Inlined + +- SelectOuter + +- While + +- DoWhile + + + +- TypeTree ----+- Synthetic + | +- Ident + | +- Select + | +- Project + | +- Singleton ++- TypeOrBoundsTree ---+ +- Refined + | +- Applied + | +- Annotated + | +- And + | +- Or + | +- MatchType + | +- ByName + | +- LambdaTypeTree + | +- Bind + | + +- TypeBoundsTree + +- SyntheticBounds + ++- CaseDef ++- TypeCaseDef + ++- Pattern --+- Value + +- Bind + +- Unapply + +- Alternative + +- TypeTest + + + +- NoPrefix ++- TypeOrBounds -+- TypeBounds + | + +- Type -------+- ConstantType + +- SymRef + +- TermRef + +- TypeRef + +- SuperType + +- Refinement + +- AppliedType + +- AnnotatedType + +- AndType + +- OrType + +- MatchType + +- ByNameType + +- ParamRef + +- ThisType + +- RecursiveThis + +- RecursiveType + +- LambdaType[ParamInfo <: TypeOrBounds] -+- MethodType + +- PolyType + +- TypeLambda + ++- ImportSelector -+- SimpleSelector + +- RenameSelector + +- OmitSelector + ++- Id + ++- Signature + ++- Position + ++- Constant + ++- Symbol --+- PackageSymbol + +- ClassSymbol + +- TypeSymbol + +- DefSymbol + +- ValSymbol + +- BindSymbol + +- NoSymbol + +Aliases: + # TermOrTypeTree = Term | TypeTree +``` + +## Other resources + +* Start plaing TASTy reflect ([link](https://github.com/nicolasstucki/tasty-reflection-exercise)) + diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 1d16c221a7b8..eeb33c3e63c1 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -51,6 +51,8 @@ sidebar: url: docs/reference/inline.html - title: Meta Programming url: docs/reference/principled-meta-programming.html + - title: TASTy Reflect + url: docs/reference/tasty-reflect.html - title: Opaque Type Aliases url: docs/reference/opaques.html - title: By-Name Implicits diff --git a/library/src/scala/tasty/reflect/Core.scala b/library/src/scala/tasty/reflect/Core.scala index a3186be0dbe9..673747076f0d 100644 --- a/library/src/scala/tasty/reflect/Core.scala +++ b/library/src/scala/tasty/reflect/Core.scala @@ -1,5 +1,6 @@ package scala.tasty.reflect +// Keep doc in syncwith docs/docs/reference/tasty-reflect.md /** Tasty reflect abstract types * * ```none diff --git a/tests/run-separate-compilation/tasty-macro-const/quoted_1.scala b/tests/run-separate-compilation/tasty-macro-const/quoted_1.scala new file mode 100644 index 000000000000..3a9acf667b72 --- /dev/null +++ b/tests/run-separate-compilation/tasty-macro-const/quoted_1.scala @@ -0,0 +1,21 @@ +import scala.quoted._ +import scala.tasty._ + +object Macros { + + inline def natConst(x: Int): Int = ~natConstImpl('(x)) + + def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { + import reflection._ + val xTree: Term = x.reflect + xTree match { + case Term.Literal(Constant.Int(n)) => + if (n <= 0) + throw new QuoteError("Parameter must be natural number") + xTree.reify[Int] + case _ => + throw new QuoteError("Parameter must be a known constant") + } + } + +} diff --git a/tests/run-separate-compilation/tasty-macro-const/quoted_2.scala b/tests/run-separate-compilation/tasty-macro-const/quoted_2.scala new file mode 100644 index 000000000000..101f5a6488c6 --- /dev/null +++ b/tests/run-separate-compilation/tasty-macro-const/quoted_2.scala @@ -0,0 +1,9 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + println(natConst(2)) + } + +} From b0408eb89b90a12ec599245235c7d042204a1fd3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 26 Nov 2018 11:13:02 +0100 Subject: [PATCH 2/6] Address comments --- docs/docs/reference/tasty-reflect.md | 32 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md index 849612af321c..32f9fa236149 100644 --- a/docs/docs/reference/tasty-reflect.md +++ b/docs/docs/reference/tasty-reflect.md @@ -3,19 +3,19 @@ layout: doc-page title: "TASTy reflect" --- -TASTy reflect provides an API that allows inspection and construction of Typed Abstract Syntax Trees (TAST). -It may be used on quoted expressions (`quoted.Expr`) and types (`quoted.Type`) from [Principled Meta-programming](./principled-meta-programming.html) +TASTy Reflect enables inspection and construction of Typed Abstract Syntax Trees (TAST). +It may be used on quoted expressions (`quoted.Expr`) and quoted types (`quoted.Type`) from [Principled Meta-programming](./principled-meta-programming.html) or on full TASTy files. -If you are starting using macros see first [Principled Meta-programming](./principled-meta-programming.html) and then follow with API (if really needed). +If you are writing macros, please first read [Principled Meta-programming](./principled-meta-programming.html). +You may find all you need without using TASTy Reflect. ## From quotes and splices to TASTs and back -`quoted.Expr` and `quoted.Type` are opaque TASTs. -The opaqueness required in [Principled Meta-programming](./principled-meta-programming.html) provide the guarantee that -the generation of code of the macro will be type correct. -Using TASTy reflect will break these guarantees and may fail at macro expansion time, hence additional explicit check must be done. +`quoted.Expr` and `quoted.Type` are only meant for generative meta-programming, generation of code without inspecting the ASTs. +[Principled Meta-programming](./principled-meta-programming.html) provides the guarantee that the generation of code will be type-correct. +Using TASTy Reflect will break these guarantees and may fail at macro expansion time, hence additional explicit check must be done. To provide reflection capabilities in macro we need to add an implicit parameter of type `scala.tasty.Reflection` and import it in the scope where it is used. @@ -32,8 +32,8 @@ def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { } ``` -`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` with return a `reflection.Term` and `reflection.TypeTree` respectivly. -It will also import all extractors and methods on TASTy reflect trees. For example the `Term.Literal(_)` extractor used bellow. +`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` which return a `reflection.Term` and `reflection.TypeTree` respectivly. +It will also import all extractors and methods on TASTy Reflect trees. For example the `Term.Literal(_)` extractor used bellow. To easily know which extractor are needed the `reflection.Term.show` method returns the string representation of the extractors. @@ -72,12 +72,16 @@ class Consumer extends TastyConsumer { Then the consumer can be instantiated with the following code to get the tree of the class `foo.Bar` for a foo in the classpath. ```scala -ConsumeTasty(classpath, List("foo.Bar"), new Consumer) +object Test { + def main(args: Array[String]): Unit = { + ConsumeTasty("", List("foo.Bar"), new Consumer) + } +} ``` -## TASTy reflect ASTs +## TASTy Reflect ASTs -TASTy reflect provides the following types as defined in `scala.tasty.reflect.Core`. +TASTy Reflect provides the following types: ```none +- Tree -+- PackageClause @@ -187,7 +191,7 @@ Aliases: # TermOrTypeTree = Term | TypeTree ``` -## Other resources +## More Examples -* Start plaing TASTy reflect ([link](https://github.com/nicolasstucki/tasty-reflection-exercise)) +* Start experimenting with TASTy Reflect ([link](https://github.com/nicolasstucki/tasty-reflection-exercise)) From d727a7b58df98170ab32c57822daac5be246638a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 26 Nov 2018 11:18:36 +0100 Subject: [PATCH 3/6] Standardize terminology --- docs/docs/reference/tasty-reflect.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md index 32f9fa236149..06ba0c1ee8ec 100644 --- a/docs/docs/reference/tasty-reflect.md +++ b/docs/docs/reference/tasty-reflect.md @@ -11,7 +11,7 @@ If you are writing macros, please first read [Principled Meta-programming](./pri You may find all you need without using TASTy Reflect. -## From quotes and splices to TASTs and back +## From quotes and splices to TASTs Reflect trees and back `quoted.Expr` and `quoted.Type` are only meant for generative meta-programming, generation of code without inspecting the ASTs. [Principled Meta-programming](./principled-meta-programming.html) provides the guarantee that the generation of code will be type-correct. @@ -56,9 +56,9 @@ The method `reflection.Term.reify[T]` provides a way to to go back to a `quoted. Note that the type must be set explicitly and that if it does not conform to it an exception will be thrown. In the code above we could have replaced `n.toExpr` by `xTree.reify[Int]`. -## ASTs of a TASTy file +## Inspect a TASTy file -To inspect the TASTy trees of a TASTy file a consumer can be defined in the following way. +To inspect the TASTy Reflect trees of a TASTy file a consumer can be defined in the following way. ```scala class Consumer extends TastyConsumer { @@ -79,7 +79,7 @@ object Test { } ``` -## TASTy Reflect ASTs +## TASTy Reflect API TASTy Reflect provides the following types: From 13e5e0d919de114854d5b1c0bb25d51380f4c12d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 26 Nov 2018 13:25:51 +0100 Subject: [PATCH 4/6] Fix typo and move text --- docs/docs/reference/tasty-reflect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md index 06ba0c1ee8ec..db8704821279 100644 --- a/docs/docs/reference/tasty-reflect.md +++ b/docs/docs/reference/tasty-reflect.md @@ -18,7 +18,7 @@ You may find all you need without using TASTy Reflect. Using TASTy Reflect will break these guarantees and may fail at macro expansion time, hence additional explicit check must be done. -To provide reflection capabilities in macro we need to add an implicit parameter of type `scala.tasty.Reflection` and import it in the scope where it is used. +To provide reflection capabilities in macros we need to add an implicit parameter of type `scala.tasty.Reflection` and import it in the scope where it is used. ```scala import scala.quoted._ @@ -34,8 +34,6 @@ def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { `import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` which return a `reflection.Term` and `reflection.TypeTree` respectivly. It will also import all extractors and methods on TASTy Reflect trees. For example the `Term.Literal(_)` extractor used bellow. -To easily know which extractor are needed the `reflection.Term.show` method returns the string representation of the extractors. - ```scala def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { @@ -52,6 +50,8 @@ def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { } ``` +To easily know which extractor are needed the `reflection.Term.show` method returns the string representation of the extractors. + The method `reflection.Term.reify[T]` provides a way to to go back to a `quoted.Expr`. Note that the type must be set explicitly and that if it does not conform to it an exception will be thrown. In the code above we could have replaced `n.toExpr` by `xTree.reify[Int]`. From 5b5ef9ac0156e0f6b00ee57c07be202e0dc6803f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 26 Nov 2018 13:33:37 +0100 Subject: [PATCH 5/6] Remove DoWhile from the docs --- docs/docs/reference/tasty-reflect.md | 1 - library/src/scala/tasty/reflect/Core.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md index db8704821279..bb481f8f5416 100644 --- a/docs/docs/reference/tasty-reflect.md +++ b/docs/docs/reference/tasty-reflect.md @@ -113,7 +113,6 @@ TASTy Reflect provides the following types: +- Inlined +- SelectOuter +- While - +- DoWhile +- TypeTree ----+- Synthetic diff --git a/library/src/scala/tasty/reflect/Core.scala b/library/src/scala/tasty/reflect/Core.scala index 673747076f0d..afc39ec36cf5 100644 --- a/library/src/scala/tasty/reflect/Core.scala +++ b/library/src/scala/tasty/reflect/Core.scala @@ -34,7 +34,6 @@ package scala.tasty.reflect * +- Inlined * +- SelectOuter * +- While - * +- DoWhile * * * +- TypeTree ----+- Synthetic From 906fc1fee8c3c575545d3d1da2536712743dff65 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 26 Nov 2018 17:06:20 +0100 Subject: [PATCH 6/6] Fix typos --- docs/docs/reference/tasty-reflect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/reference/tasty-reflect.md b/docs/docs/reference/tasty-reflect.md index bb481f8f5416..39dcf98311bc 100644 --- a/docs/docs/reference/tasty-reflect.md +++ b/docs/docs/reference/tasty-reflect.md @@ -32,8 +32,8 @@ def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { } ``` -`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` which return a `reflection.Term` and `reflection.TypeTree` respectivly. -It will also import all extractors and methods on TASTy Reflect trees. For example the `Term.Literal(_)` extractor used bellow. +`import reflection._` will provide a `reflect` extension method on `quoted.Expr` and `quoted.Type` which return a `reflection.Term` and `reflection.TypeTree` respectively. +It will also import all extractors and methods on TASTy Reflect trees. For example the `Term.Literal(_)` extractor used below. ```scala def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { @@ -50,7 +50,7 @@ def natConstImpl(x: Expr[Int])(implicit reflection: Reflection): Expr[Int] = { } ``` -To easily know which extractor are needed the `reflection.Term.show` method returns the string representation of the extractors. +To easily know which extractors are needed, the `reflection.Term.show` method returns the string representation of the extractors. The method `reflection.Term.reify[T]` provides a way to to go back to a `quoted.Expr`. Note that the type must be set explicitly and that if it does not conform to it an exception will be thrown.