import Lean.Parser.Extension
import Lean.Elab.Term
/-!
# Testing string gaps in string literals
String gaps are described in RFC #2838
-/
/-!
A string gap with no trailing space.
-/
/-- info: "ab" -/
#guard_msgs in
#eval "a\
b"
/-!
A string gap with trailing space before the `b`, which is consumed.
-/
/-- info: "ab" -/
#guard_msgs in
#eval "a\
b"
/-!
A string gap with space before the gap, which is not consumed.
-/
/-- info: "a b" -/
#guard_msgs in
#eval "a \
b"
/-!
Multiple string gaps in a row.
-/
/-- info: "a b" -/
#guard_msgs in
#eval "a \
\
\
b"
/-!
Two tests from the RFC.
-/
/-- info: "this is a string" -/
#guard_msgs in
#eval "this is \
a string"
/-- info: "this is a string" -/
#guard_msgs in
#eval "this is \
a string"
/-!
Two examples of how spaces are accounted for in string gaps. `\x20` is a way to force a leading space.
-/
/-- info: "there are three spaces between the brackets < >" -/
#guard_msgs in
#eval "there are three spaces between the brackets < \
>"
/-- info: "there are three spaces between the brackets < >" -/
#guard_msgs in
#eval "there are three spaces between the brackets <\
\x20 >"
/-!
Using `\n` to terminate a string gap, which is a technique suggested by Mario for using string gaps to write
multiline literals in an indented context.
-/
/-- info: "this is\n a string with two space indent" -/
#guard_msgs in
#eval "this is\
\n a string with two space indent"
/-!
Similar tests but for interpolated strings.
-/
/-- info: "ab" -/
#guard_msgs in
#eval s!"a\
b"
/-- info: "ab" -/
#guard_msgs in
#eval s!"a\
b"
/-- info: "ab" -/
#guard_msgs in
#eval s!"a\
b"
/-!
The `{` terminates the string gap.
-/
/-- info: "ab" -/
#guard_msgs in
#eval s!"a\
{"b"}\
"
open Lean
/-!
## Testing whitespace handling with specific line terminators
-/
/-!
Standard string gap, with LF
-/
/-- info: "ab" -/
#guard_msgs in
#eval show MetaM String from do
let stx ← ofExcept <| Parser.runParserCategory (← getEnv) `term "\"a\\\n b\""
let some s := stx.isStrLit? | failure
return s
/-!
Isolated CR, which is an error
-/
/-- error: :1:3: invalid escape sequence -/
#guard_msgs (error, drop info) in
#eval show MetaM String from do
let stx ← ofExcept <| Parser.runParserCategory (← getEnv) `term "\"a\\\r b\""
let some s := stx.isStrLit? | failure
return s
/-!
Not a string gap since there's no end-of-line.
-/
/-- error: :1:3: invalid escape sequence -/
#guard_msgs (error, drop info) in
#eval show MetaM String from do
let stx ← ofExcept <| Parser.runParserCategory (← getEnv) `term "\"a\\ b\""
let some s := stx.isStrLit? | failure
return s
/-!
## Scala-style stripMargin
This is a test that string gaps could be paired with a new string elaboration syntax
for indented multiline string literals.
-/
def String.dedent (s : String) : Option String :=
let parts := s.split '\n' |>.map String.Slice.trimAsciiStart |>.toList
match parts with
| [] => ""
| [p] => p.copy
| p₀ :: parts =>
if !parts.all (·.startsWith "|") then
none
else
p₀.copy ++ "\n" ++ "\n".toSlice.intercalate (parts.map fun p => p.drop 1)
elab "d!" s:str : term => do
let some s := s.raw.isStrLit? | Lean.Elab.throwIllFormedSyntax
let some s := String.dedent s | Lean.Elab.throwIllFormedSyntax
pure $ Lean.mkStrLit s
/-- info: "this is line 1\n line 2, indented\nline 3" -/
#guard_msgs in
#eval d!"this is \
line 1
| line 2, indented
|line 3"