Conversation
1305673 to
a2363f8
Compare
Codecov Report
@@ Coverage Diff @@
## main #339 +/- ##
==========================================
- Coverage 96.56% 96.37% -0.20%
==========================================
Files 9 9
Lines 1049 1049
Branches 94 94
==========================================
- Hits 1013 1011 -2
- Misses 36 38 +2
Continue to review full report at Codecov.
|
|
cc @s5bug |
johnynek
left a comment
There was a problem hiding this comment.
This is really awesome! I'm so excited you tackled this.
| object StringInterpolation { | ||
|
|
||
| implicit class Helper(val sc: StringContext) extends AnyVal { | ||
| def parser(args: Any*): Parser[Any] = macro StringInterpolationMacros.parser |
There was a problem hiding this comment.
I am not very familiar with macros honestly... I always have to look up details.
So, I guess that since this is a white box macro that the type gets refined (as checked in the test below)?
There was a problem hiding this comment.
Yup correct, this needs to be a whitebox macro to get the type refined at each call site.
| val (matched, last) = stringParts.splitAt(args.length) | ||
|
|
||
| val parsers: List[Tree] = matched.zip(args).map { case (a, b) => | ||
| if (a.isEmpty) b.tree else q"${lit(a)} *> $b " |
There was a problem hiding this comment.
I'm wondering about what happens if b is a Parser0. It seems we could often handle it (as long as there is at least one string value, or another Parser. But I guess we can't get the type of b right?
There was a problem hiding this comment.
should we test that with Parser0? I guess we could make sure it works as long as there is a leading Parser or string?
| assertEquals( | ||
| arrow.parseAll("spam,1234 -> quux"), | ||
| Right((true, BigInt(1234), 42)) | ||
| ) |
There was a problem hiding this comment.
could we add a few assertions on compileErrors(...)
https://scalameta.org/munit/docs/assertions.html
it would be good to see some failures and make sure they fail in both scala 2 and 3 (although the messages may be different).
| val foo: Parser[Boolean] = Parser.string("spam").as(true) | Parser.string("eggs").as(false) | ||
| val bar: Parser[BigInt] = Numbers.bigInt | ||
| val quux: Parser[Int] = Parser.string("quux").as(42) | ||
|
|
There was a problem hiding this comment.
can we exercise with 0 args, 1 arg up to say 3 (to exercise non-tuples)
so, I guess val x: Parser[Unit] = parser"foo" should work. val x: Parser[A] = parser"foo$aParser" and then at 2 we should have tuples?
or do we require 2 or more (or 1 if we wrap with tuple1)?
There was a problem hiding this comment.
Wrote tests to exercise these cases. Here's how it currently works:
- empty string: compile error
- non-empty string with no interpolated parsers:
Parser[Unit]equivalent toParser.string("...") - string with single interpolated
Parser[A]:Parser[A] - string with n interpolated
Parser[{A, B, C...}]:Parser[(A, B, C...)]
| test("parser string interpolator") { | ||
| val foo: Parser[Boolean] = Parser.string("spam").as(true) | Parser.string("eggs").as(false) | ||
| val bar: Parser[BigInt] = Numbers.bigInt | ||
| val quux: Parser[Int] = Parser.string("quux").as(42) |
There was a problem hiding this comment.
could we write this as parser"quux".as(42)? does parser bind tighter than method application?
There was a problem hiding this comment.
Yes, it does
|
This is awesome! Thank you so much! |
|
Added more code to deal with A change from previous discussions is that |
regadas
left a comment
There was a problem hiding this comment.
this looks great! thanks for taking this 😄 just left some minor things.
I was thinking ... would it be worth separating this into it's own module?
|
|
||
| object StringInterpolation { | ||
|
|
||
| implicit class Helper(val sc: StringContext) extends AnyVal { |
There was a problem hiding this comment.
| implicit class Helper(val sc: StringContext) extends AnyVal { | |
| implicit class Helper(private val sc: StringContext) extends AnyVal { |
| transparent inline final def parser(inline args: Any*): Any = ${ parserImpl('sc, 'args) } | ||
| } | ||
|
|
||
| def parserImpl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using q: Quotes) = { |
There was a problem hiding this comment.
| def parserImpl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using q: Quotes) = { | |
| private def parserImpl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using q: Quotes) = { |
|
|
||
| } | ||
|
|
||
| test("including a Parser0") { |
There was a problem hiding this comment.
could we also assert val p: Parser0[Unit] = parser"" ?
|
|
||
| } | ||
|
|
||
| class StringInterpolationMacros(val c: whitebox.Context) { |
There was a problem hiding this comment.
I believe we could make this one private
| class StringInterpolationMacros(val c: whitebox.Context) { | |
| private class StringInterpolationMacros(val c: whitebox.Context) { |
|
Rather than nit on this, I'm going to merge and then will iterate. I'll send a followup PR with @regadas 's suggestions. Thanks so much! I look forward to a release with this! |
|
Well, it's functional now in but I thought on it, and: the |
|
@stephenjudkins take a look at my PR where I also tried to factor the code similarly and added a few more tests. |
Fixes #329.
I just dragged this over the finish line and am open to any suggested changes. Was a useful if somewhat painful way to dip my toes into Scala 3