diff --git a/effekt/jvm/src/test/scala/effekt/LexerTests.scala b/effekt/jvm/src/test/scala/effekt/LexerTests.scala index 0c335b422..0c70e5781 100644 --- a/effekt/jvm/src/test/scala/effekt/LexerTests.scala +++ b/effekt/jvm/src/test/scala/effekt/LexerTests.scala @@ -240,6 +240,33 @@ class LexerTests extends munit.FunSuite { ) } + test("string splice hole") { + val prog = "<${x}>" + assertTokensEq( + prog, + `<`, `${`, Ident("x"), `}$`, `>`, + EOF + ) + } + + test("all holes") { + val prog = "<{<\"str\">; <>}>" + assertTokensEq( + prog, + `<{`, HoleStr("str"), `;`, `<>`, `}>`, + EOF + ) + } + + test("all holes imbriqued") { + val prog = "<{< <\"str\"> >}>" + assertTokensEq( + prog, + `<{`, `<`, HoleStr("str"), `>`, `}>`, + EOF + ) + } + test("multiline string holes") { val prog1: String = """<" Here it starts diff --git a/effekt/shared/src/main/scala/effekt/Lexer.scala b/effekt/shared/src/main/scala/effekt/Lexer.scala index 73ba5828e..5dc6ea945 100644 --- a/effekt/shared/src/main/scala/effekt/Lexer.scala +++ b/effekt/shared/src/main/scala/effekt/Lexer.scala @@ -227,7 +227,7 @@ object Position { * interpolation boundaries. When we see ${, we record the current brace depth * and know the interpolation ends when we return to that depth. */ -case class DepthTracker(var parens: Int, var braces: Int, var brackets: Int) +case class DepthTracker(var parens: Int, var braces: Int, var brackets: Int, var holes: Int) /** * Never throws exceptions - always returns Error tokens for errors. @@ -246,7 +246,7 @@ class Lexer(source: Source) extends Iterator[Token] { // String interpolation state private val delimiters = mutable.Stack[Delimiter]() - private val depthTracker = DepthTracker(0, 0, 0) + private val depthTracker = DepthTracker(0, 0, 0, 0) private val interpolationDepths = mutable.Stack[Int]() /** @@ -444,7 +444,9 @@ class Lexer(source: Source) extends Iterator[Token] { case ('<', '<') => advance2With(TokenKind.`<<`) case ('<', '=') => advance2With(TokenKind.`<=`) case ('<', '>') => advance2With(TokenKind.`<>`) - case ('<', '{') => advance2With(TokenKind.`<{`) + case ('<', '{') => + depthTracker.holes += 1 + advance2With(TokenKind.`<{`) case ('<', '~') => advance2With(TokenKind.`<~`) case ('<', _) => advanceWith(TokenKind.`<`) @@ -480,7 +482,9 @@ class Lexer(source: Source) extends Iterator[Token] { case ('$', _) => advanceWith(TokenKind.Error(LexerError.UnknownChar('$'))) - case ('}', '>') => advance2With(TokenKind.`}>`) + case ('}', '>') if depthTracker.holes > 0 => + depthTracker.holes -= 1 + advance2With(TokenKind.`}>`) case ('}', _) if isAtInterpolationBoundary => interpolationDepths.pop() depthTracker.braces -= 1 diff --git a/examples/pos/issue1301.check b/examples/pos/issue1301.check new file mode 100644 index 000000000..9709e4251 --- /dev/null +++ b/examples/pos/issue1301.check @@ -0,0 +1,2 @@ +<1> +test \ No newline at end of file diff --git a/examples/pos/issue1301.effekt b/examples/pos/issue1301.effekt new file mode 100644 index 000000000..0dfdcd2e2 --- /dev/null +++ b/examples/pos/issue1301.effekt @@ -0,0 +1,12 @@ +def main() = { + def f() = <{def g() = <{<"myFun">}>; 42}> + val x = println(s"<${1.show}>") + def g() = <{ + val x = 42 + <" here's a string "> + println(x) + <> + }> + val x = println(s"${"test"}") + def h() = println(s"<${"1" ++ <"string"> ++ <>}>") + () +} \ No newline at end of file