Skip to content

Commit 676820e

Browse files
committed
Fix parsing of nested implied indentations with ...
1 parent 9ea94f1 commit 676820e

File tree

3 files changed

+76
-20
lines changed

3 files changed

+76
-20
lines changed

hkmc2/shared/src/main/scala/hkmc2/syntax/Lexer.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,12 +368,19 @@ class Lexer(origin: Origin, dbg: Bool)(using raise: Raise):
368368
/** Converts the lexed tokens into structured tokens. */
369369
lazy val bracketedTokens: Ls[Stroken -> Loc] =
370370
import BracketKind._
371-
def go(toks: Ls[Token -> Loc], canStartAngles: Bool, stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]], acc: Ls[Stroken -> Loc]): Ls[Stroken -> Loc] =
371+
def go(
372+
toks: Ls[Token -> Loc],
373+
canStartAngles: Bool,
374+
stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]],
375+
acc: Ls[Stroken -> Loc],
376+
): Ls[Stroken -> Loc] =
372377
toks match
373378
case (SUSPENSION(true), l0) :: Nil =>
379+
// * This is an ugly special-case to handle things like `module M with ...`
380+
// * where there is no actual body after the `...`.
381+
// * It can't be handled in the parser because this is only valid at the top-level,
382+
// * not within brackets, as in `(arg0, ...) => blah`.
374383
go(OPEN_BRACKET(Indent) -> l0 :: LITVAL(Tree.UnitLit(true)) -> l0 :: Nil, false, stack, acc)
375-
case (SUSPENSION(true), l0) :: (NEWLINE, l1) :: rest =>
376-
go(OPEN_BRACKET(Indent) -> (l0 ++ l1) :: rest, false, stack, acc)
377384
case (QUOTE, l0) :: (IDENT("<", true), l1) :: rest =>
378385
go(rest, false, stack, (IDENT("<", true), l1) :: (QUOTE, l0) :: acc)
379386
case (QUOTE, l0) :: (IDENT(">", true), l1) :: rest =>

hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,19 @@ abstract class Parser(
126126
doPrintDbg("" * this.indent + msg)
127127

128128
protected var indent = 0
129-
private var _cur: Ls[TokLoc] = tokens
129+
private var _cur: Ls[TokLoc] = expandSuspensions(tokens)
130+
131+
// * Expands end-of-line suspensions that introduce implied indentation.
132+
private def expandSuspensions(tokens: Ls[TokLoc]): Ls[TokLoc] = tokens match
133+
case (SUSPENSION(true), l0) :: (NEWLINE, l1) :: rest =>
134+
val outerLoc = l0.left ++ rest.lastOption.map(_._2.right)
135+
val innerLoc = l1.right ++ rest.lastOption.map(_._2.left)
136+
BRACKETS(Indent, expandSuspensions(rest))(innerLoc) -> outerLoc :: Nil
137+
case tl :: rest =>
138+
val rest2 = expandSuspensions(rest)
139+
if rest2 is rest then tokens
140+
else tl :: rest2
141+
case Nil => tokens
130142

131143
private def wrap[R](args: => Any)(using l: Line, n: Name)(mkRes: => R): R =
132144
printDbg(s"@ ${n.value}${args match {
Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
1-
:parseOnly
1+
:js
22

33

4-
log of ...
4+
print of ...
55
123
6-
//│ Parsed:
7-
//│ App(Ident(log),Tup(List(IntLit(123))))
6+
//│ > 123
87

9-
log of...
8+
print of...
109
123
11-
//│ Parsed:
12-
//│ App(Ident(log),Tup(List(IntLit(123))))
10+
//│ > 123
1311

14-
log of
12+
print of
1513
123
16-
//│ Parsed:
17-
//│ App(Ident(log),Tup(List(IntLit(123))))
14+
//│ > 123
15+
16+
17+
if true do...
18+
19+
if true do...
20+
1
21+
//│ = 1
1822

1923

2024
module Option with ...
21-
//│ Parsed:
22-
//│ TypeDef(Mod,Ident(Option),None,Some(Block(List(UnitLit(true)))))
2325

2426
module Option with ...
2527
val a = 1
26-
//│ Parsed:
27-
//│ TypeDef(Mod,Ident(Option),None,Some(Block(List(TermDef(ImmutVal,Ident(a),Some(IntLit(1)))))))
2828

2929
module Option with ...
3030
//
3131
val a = 1
3232
//
33-
//│ Parsed:
34-
//│ TypeDef(Mod,Ident(Option),None,Some(Block(List(TermDef(ImmutVal,Ident(a),Some(IntLit(1)))))))
3533

3634
:pt
3735
module A with
@@ -41,6 +39,7 @@ module A with
4139
val a = 1
4240

4341
val b = 2
42+
4443
//│ Parsed tree:
4544
//│ TypeDef:
4645
//│ k = Mod
@@ -62,4 +61,42 @@ module A with
6261
//│ rhs = S of IntLit of 2
6362

6463

64+
fun test(x) =
65+
if x then print("ok") else...
66+
print("ko")
67+
let foo = 1
68+
//│ foo = 1
69+
70+
71+
:w
72+
x =>
73+
if false then 0 else...
74+
1
75+
let bar = 1
76+
//│ ╔══[WARNING] Pure expression in statement position
77+
//│ ║ l.72: x =>
78+
//│ ║ ^^^^
79+
//│ ║ l.73: if false then 0 else...
80+
//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^
81+
//│ ║ l.74: 1
82+
//│ ╙── ^^^
83+
//│ bar = 1
84+
85+
bar
86+
//│ = 1
87+
88+
89+
:w
90+
:e
91+
x => if false then 0 else...
92+
1
93+
let foo = 1
94+
//│ ╔══[ERROR] Illegal position for '...' spread operator.
95+
//│ ║ l.92: 1
96+
//│ ╙── ^
97+
//│ ╔══[WARNING] Pure expression in statement position
98+
//│ ║ l.91: x => if false then 0 else...
99+
//│ ╙── ^
100+
//│ foo = 1
101+
65102

0 commit comments

Comments
 (0)