Skip to content

Commit 698b0d5

Browse files
committed
refactor: use parser restarting API instead of whitespace hacks.
This is possible after leanprover/lean4#10043 improved upstream parsing API. This replaces the current use of `parserInputString` and seems clearer overall. There are two main cases for string literals for us to process: - string literals coming from Verso: in this case, `.getPos` and `.getTailPos` do provide the correct start / end of the string, without the quotes. - string literals coming from Lean: in this case, we must account for the quotes and fixup the positions. We have handled this by convention, but `VersoUtils.parseString` takes an optional parameter as it is used in both modes. Eventually we'd like to move Verso-style strings to its own type instead of `StrLit`. Notes: - I couldn't port `VersoBlog.leanInit` as `Parser.parseHeader` always parses from `pos := 0`. This is the last blocker to completely remove `parserInputString`. - In general, it seems like most functions using `mkParserState` upstream could benefit from an update to take positions. - Tests in `UsersGuide.Markup` had to be adapted due to use of `contents.getString.trimAsciiEnd.copy`. I did this to pass CI, must implement a better fix before merging. - Code that depends on MD4Lean hasn't been ported as MD4 doesn't seem to provide the right API for us. - TODO: we should test `canonical := true` and quoting properly. Note special cases such as `` `code ``
1 parent 04ce069 commit 698b0d5

8 files changed

Lines changed: 117 additions & 84 deletions

File tree

doc/UsersGuide/Markup.lean

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ def markupPreview : DirectiveExpanderOf MarkupPreviewConfig
291291
let `(block|``` | $expected ```) := blk2
292292
| throwErrorAt blk1 "Expected anonymous code block"
293293

294-
let stx ← blocks {} |>.parseString contents.getString.trimAsciiEnd.copy
294+
-- XXX Fixme due to trimAsciiEnd
295+
-- let stx ← blocks {} |>.parseString contents.getString.trimAsciiEnd.copy
296+
let stx ← blocks {} |>.parseString contents (versoStyle := true)
295297
let p ← preview stx
296298
let p := p.pretty (width := 35)
297299

@@ -327,7 +329,7 @@ open Verso.Parser in
327329
def markupPreviewPre : CodeBlockExpanderOf MarkupPreviewConfig
328330
| {title}, contents => do
329331

330-
let stx ← blocks {} |>.parseString contents.getString
332+
let stx ← blocks {} |>.parseString contents (versoStyle := true)
331333
let p ← preview stx
332334
let p := p.pretty (width := 35)
333335

@@ -430,7 +432,7 @@ Metadata blocks begin and end with `%%%`, and they contain any syntax that would
430432
a b c
431433
```
432434
```
433-
<p> a b c </p>
435+
<p> a b c </p>
434436
```
435437
:::
436438

@@ -690,7 +692,7 @@ A description item is a line that starts with zero or more spaces, followed by a
690692

691693
<dt> Item 2 </dt>
692694
<dd>
693-
<p> Description of item 2 </p>
695+
<p> Description of item 2 </p>
694696
</dd>
695697
</dl>
696698
```
@@ -729,7 +731,7 @@ But not this one.
729731
<p> So is this one. </p>
730732
</blockquote>
731733

732-
<p> But not this one. </p>
734+
<p> But not this one. </p>
733735
```
734736
:::
735737

@@ -941,6 +943,7 @@ Hyperlinks consist of the link text in square brackets followed by the target in
941943
<a href="https://lean-lang.org">
942944
Lean
943945
</a>
946+
944947
</p>
945948
```
946949
:::
@@ -987,7 +990,9 @@ This makes it possible to represent values that begin or end with back-ticks:
987990
`` `quotedName ``
988991
```
989992
```
990-
<p> <code>"`quotedName"</code> </p>
993+
<p>
994+
<code>"`quotedName"</code>
995+
</p>
991996
```
992997
:::
993998
or with spaces:
@@ -996,7 +1001,9 @@ or with spaces:
9961001
`` one space ``
9971002
```
9981003
```
999-
<p> <code>" one space "</code> </p>
1004+
<p>
1005+
<code>" one space "</code>
1006+
</p>
10001007
```
10011008
:::
10021009

@@ -1011,6 +1018,7 @@ Images require both alternative text and an address for the image:
10111018
<img
10121019
src="image.png"
10131020
alt="Alt text"/>
1021+
10141022
</p>
10151023
```
10161024
:::
@@ -1057,6 +1065,7 @@ $`\frac{1}{2}`-powered syntax]
10571065
<math contents="\\frac{1}{2}"/>
10581066
-powered syntax
10591067
</ref>
1068+
10601069
</p>
10611070
```
10621071
:::
@@ -1071,6 +1080,7 @@ This one takes a single inline code element without needing square brackets:
10711080
<lean >
10721081
<code>"2 + f 4"</code>
10731082
</lean>
1083+
10741084
</p>
10751085
```
10761086
:::

src/verso-blog/VersoBlog.lean

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace Verso.Genre.Blog
2929
open Lean.Doc.Syntax
3030
open Verso ArgParse Doc Elab
3131
open Lean Elab
32-
open Verso.SyntaxUtils (parserInputString)
32+
open Verso.SyntaxUtils (inputContextFromStrLit)
3333

3434
open SubVerso.Examples (loadExamples Example)
3535
open SubVerso.Examples.Messages (messagesMatch)
@@ -434,9 +434,32 @@ instance [Monad m] [MonadInfoTree m] [MonadLiftT CoreM m] [MonadEnv m] [MonadErr
434434
.flag `showProofStates true "Show proof states in rendered page?"
435435

436436

437+
def parserInputString [Monad m] [MonadFileMap m]
438+
(str : TSyntax `str) :
439+
m String := do
440+
let text ← getFileMap
441+
let preString := (0 : String.Pos.Raw).extract text.source (str.raw.getPos?.getD 0)
442+
let mut code := ""
443+
let mut iter := preString.startPos
444+
while h : iter ≠ preString.endPos do
445+
let c := iter.get h
446+
iter := iter.next h
447+
if c == '\n' then
448+
code := code.push '\n'
449+
else
450+
for _ in [0:c.utf8Size] do
451+
code := code.push ' '
452+
let strOriginal? : Option String := do
453+
let ⟨start, stop⟩ ← str.raw.getRange?
454+
start.extract text.source stop
455+
code := code ++ strOriginal?.getD str.getString
456+
return code
457+
437458
@[code_block]
438459
def leanInit : CodeBlockExpanderOf LeanInitBlockConfig
439460
| config , str => withTraceNode `Elab.Verso.block.lean (fun _ => pure m!"leanInit") <| do
461+
-- XXX [upstream]: can't use pos here due to `Parser.parseHeader` type
462+
let (_pos, _context) ← inputContextFromStrLit str
440463
let context := Parser.mkInputContext (← parserInputString str) (← getFileName)
441464
let (header, state, msgs) ← Parser.parseHeader context
442465
if !header.raw[0].isNone then
@@ -471,12 +494,15 @@ open SubVerso.Highlighting Highlighted in
471494
def lean : CodeBlockExpanderOf LeanBlockConfig
472495
| config, str => withTraceNode `Elab.Verso.block.lean (fun _ => pure m!"lean block") <| withoutAsync do
473496
let x := config.exampleContext
474-
let (commandState, state) ← match exampleContextExt.getState (← getEnv) |>.contexts.find? x.getId with
497+
let (commandState, _state) ← match exampleContextExt.getState (← getEnv) |>.contexts.find? x.getId with
475498
| some (.inline commandState state) => pure (commandState, state)
476499
| some (.subproject ..) => throwErrorAt x "Expected an example context for inline Lean, but found a subproject"
477500
| some (.module ..) => throwErrorAt x "Expected an example context for inline Lean, but found a module"
478501
| none => throwErrorAt x "Can't find example context"
479-
let context := Parser.mkInputContext (← parserInputString str) (← getFileName)
502+
503+
-- XXX: note state above unused, remove once we verify everything is fine
504+
let (pos, context) ← inputContextFromStrLit str
505+
let state := { pos }
480506
-- Process with empty messages to avoid duplicate output
481507
let s ←
482508
withTraceNode `Elab.Verso.block.lean (fun _ => pure m!"Elaborating commands") <|
@@ -616,9 +642,6 @@ def leanInline : RoleExpanderOf LeanInlineConfig
616642
let {env, scopes, ngen, ..} := commandState
617643
let {openDecls, currNamespace, opts, ..} := scopes.head!
618644

619-
620-
let altStr ← parserInputString str
621-
622645
let leveller {α} : TermElabM α → TermElabM α :=
623646
if let some us := config.universes then
624647
let us :=
@@ -627,7 +650,7 @@ def leanInline : RoleExpanderOf LeanInlineConfig
627650
Elab.Term.withLevelNames us
628651
else id
629652

630-
match (← SyntaxUtils.runParserCategory `term altStr) with
653+
match (← SyntaxUtils.runParserCategory `term str) with
631654
| .error e => throwErrorAt str e
632655
| .ok stx => withOptions (fun _ => opts) <| runWithOpenDecls scopes <| runWithVariables scopes fun _ => do
633656
let (newMsgs, type, tree) ← do
@@ -637,7 +660,7 @@ def leanInline : RoleExpanderOf LeanInlineConfig
637660
let (tree', t) ← do
638661

639662
let expectedType ← config.type.mapM fun (s : StrLit) => do
640-
match (← SyntaxUtils.runParserCategory `term s.getString) with
663+
match (← SyntaxUtils.runParserCategory `term s) with
641664
| .error e => throwErrorAt str e
642665
| .ok stx => withEnableInfoTree false do
643666
let t ← leveller <| Elab.Term.elabType stx

src/verso-manual/VersoManual/Imports.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ Parses, but does not validate, a module header.
3131
@[code_block]
3232
def imports : CodeBlockExpanderOf ImportsParams
3333
| { «show» } , str => do
34-
let altStr ← parserInputString str
3534
let p := Parser.whitespace >> Parser.Module.header.fn
36-
let headerStx ← p.parseString altStr
35+
let headerStx ← p.parseString str
3736
let hl ← highlight headerStx #[] {}
3837
if «show» then
3938
``(Block.other (Block.lean $(quote hl) {}) #[Block.code $(quote str.getString)])

src/verso-manual/VersoManual/InlineLean.lean

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ open Verso ArgParse Doc Elab Genre.Manual Html Code Highlighted.WebAssets Expect
2828
open Lean Elab
2929
open SubVerso.Highlighting
3030

31-
open Verso.SyntaxUtils (parserInputString SyntaxError)
31+
open Verso.SyntaxUtils (inputContextFromStrLit)
3232

3333
open Lean.Doc.Syntax
3434
open Lean.Elab.Tactic.GuardMsgs
@@ -237,13 +237,13 @@ def elabCommands (config : LeanBlockConfig) (str : StrLit)
237237
let origScopes := origScopes.modifyHead fun sc =>
238238
{ sc with opts := pp.tagAppFns.set (Elab.async.set sc.opts false) true }
239239

240-
let altStr ← parserInputString str
240+
let (pos, ictx) ← inputContextFromStrLit str
241241

242-
let ictx := Parser.mkInputContext altStr (← getFileName)
243-
let cctx : Command.Context := { fileName := ← getFileName, fileMap := FileMap.ofString altStr, snap? := none, cancelTk? := none}
242+
let fileMap ← getFileMap
243+
let cctx : Command.Context := { fileName := ← getFileName, fileMap, snap? := none, cancelTk? := none}
244244

245245
let mut cmdState : Command.State := {env := ← getEnv, maxRecDepth := ← MonadRecDepth.getMaxRecDepth, scopes := origScopes}
246-
let mut pstate := {pos := 0, recovering := false}
246+
let mut pstate := {pos}
247247
let mut cmds := #[]
248248

249249
repeat
@@ -346,8 +346,6 @@ Elaborates the provided Lean term in the context of the current Verso module.
346346
def leanTerm : CodeBlockExpanderOf LeanInlineConfig
347347
| config, str => withoutAsync <| do
348348

349-
let altStr ← parserInputString str
350-
351349
let leveller :=
352350
if let some us := config.universes then
353351
let us :=
@@ -356,7 +354,7 @@ def leanTerm : CodeBlockExpanderOf LeanInlineConfig
356354
Elab.Term.withLevelNames us
357355
else id
358356

359-
match (← SyntaxUtils.runParserCategory `term altStr) with
357+
match (← SyntaxUtils.runParserCategory `term str) with
360358
| .error e => throwErrorAt str e
361359
| .ok stx =>
362360
let (newMsgs, tree) ← do
@@ -365,15 +363,15 @@ def leanTerm : CodeBlockExpanderOf LeanInlineConfig
365363
Core.resetMessageLog
366364

367365
let tree' ← runWithOpenDecls <| runWithVariables fun _vars => do
368-
let expectedType ← config.type.mapM fun (s : StrLit) => do
369-
match (← SyntaxUtils.runParserCategory `term s.getString) with
366+
let expectedType ← config.type.mapM fun (str : StrLit) => do
367+
match (← SyntaxUtils.runParserCategory `term str) with
370368
| .error e => throwErrorAt stx e
371369
| .ok stx => withEnableInfoTree false do
372370
let t ← leveller <| Elab.Term.elabType stx
373371
Term.synthesizeSyntheticMVarsNoPostponing
374372
let t ← instantiateMVars t
375373
if t.hasExprMVar || t.hasLevelMVar then
376-
throwErrorAt s "Type contains metavariables: {t}"
374+
throwErrorAt str "Type contains metavariables: {t}"
377375
pure t
378376

379377
let e ← Elab.Term.elabTerm (catchExPostpone := true) stx expectedType
@@ -423,7 +421,6 @@ def leanInline : RoleExpanderOf LeanInlineConfig
423421
| throwError "Expected exactly one argument"
424422
let `(inline|code( $term:str )) := arg
425423
| throwErrorAt arg "Expected code literal with the example name"
426-
let altStr ← parserInputString term
427424

428425
let leveller :=
429426
if let some us := config.universes then
@@ -433,7 +430,7 @@ def leanInline : RoleExpanderOf LeanInlineConfig
433430
Elab.Term.withLevelNames us
434431
else id
435432

436-
match (← SyntaxUtils.runParserCategory `term altStr) with
433+
match (← SyntaxUtils.runParserCategory `term term) with
437434
| .error e => throwErrorAt term e
438435
| .ok stx =>
439436

@@ -443,15 +440,15 @@ def leanInline : RoleExpanderOf LeanInlineConfig
443440
Core.resetMessageLog
444441
let (tree', t) ← runWithOpenDecls <| runWithVariables fun _ => do
445442

446-
let expectedType ← config.type.mapM fun (s : StrLit) => do
447-
match (← SyntaxUtils.runParserCategory `term s.getString) with
443+
let expectedType ← config.type.mapM fun (str : StrLit) => do
444+
match (← SyntaxUtils.runParserCategory `term str) with
448445
| .error e => throwErrorAt term e
449446
| .ok stx => withEnableInfoTree false do
450447
let t ← leveller <| Elab.Term.elabType stx
451448
Term.synthesizeSyntheticMVarsNoPostponing
452449
let t ← instantiateMVars t
453450
if t.hasExprMVar || t.hasLevelMVar then
454-
throwErrorAt s "Type contains metavariables: {t}"
451+
throwErrorAt str "Type contains metavariables: {t}"
455452
pure t
456453

457454
let e ← leveller <| Elab.Term.elabTerm (catchExPostpone := true) stx expectedType
@@ -514,9 +511,8 @@ def inst : RoleExpanderOf LeanBlockConfig
514511
| throwError "Expected exactly one argument"
515512
let `(inline|code( $term:str )) := arg
516513
| throwErrorAt arg "Expected code literal with the example name"
517-
let altStr ← parserInputString term
518514

519-
match (← SyntaxUtils.runParserCategory `term altStr) with
515+
match (← SyntaxUtils.runParserCategory `term term) with
520516
| .error e => throwErrorAt term e
521517
| .ok stx =>
522518
let (newMsgs, tree) ← do

src/verso-manual/VersoManual/InlineLean/Signature.lean

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ open SubVerso.Highlighting
1515
open Verso Genre Manual ArgParse Doc Elab
1616
open Verso Output Html
1717
open Verso Code Highlighted WebAssets
18-
open Verso.SyntaxUtils (parserInputString)
1918
open Lean Elab
2019

2120
namespace Verso.Genre.Manual.InlineLean
@@ -59,11 +58,9 @@ end
5958
@[code_block]
6059
def signature : CodeBlockExpanderOf SignatureConfig
6160
| {«show»}, str => withoutAsync do
62-
let altStr ← parserInputString str
6361
let col? := (← getRef).getPos? |>.map (← getFileMap).utf8PosToLspPos |>.map (·.character)
6462

65-
66-
match (← SyntaxUtils.runParserCategory `signature_spec altStr) with
63+
match (← SyntaxUtils.runParserCategory `signature_spec str) with
6764
| .error e => throwError e
6865
| .ok stx =>
6966
let `(signature_spec|$[$kw]? $name:declId $sig:declSig) := stx

src/verso-manual/VersoManual/InlineLean/SyntaxError.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,8 @@ def syntaxError : CodeBlockExpanderOf SyntaxErrorConfig
139139
(kind := Lsp.SymbolKind.file)
140140
(detail? := some "Syntax error")
141141

142-
let s := str.getString
143142
let errorFn := SyntaxUtils.runParserCategory.toSyntaxErrors
144-
match (← SyntaxUtils.runParserCategoryGen (errorFn := errorFn) config.category s) with
143+
match (← SyntaxUtils.runParserCategoryGen (errorFn := errorFn) config.category str) with
145144
| .ok stx =>
146145
throwErrorAt str m!"Expected a syntax error for category {config.category}, but got {indentD stx}"
147146
| .error es =>
@@ -151,6 +150,7 @@ def syntaxError : CodeBlockExpanderOf SyntaxErrorConfig
151150
saveOutputs config.name msgs
152151
Hover.addCustomHover (← getRef) <| MessageData.joinSep (msgs.map fun ⟨sev, msg⟩ => m!"{sevStr sev.toSeverity}:{indentD msg.toString}") Format.line
153152

153+
let s := str.getString
154154
`(Block.other {Block.syntaxError with data := ToJson.toJson ($(quote s), $(quote es))} #[Block.code $(quote s)])
155155
where
156156
sevStr : MessageSeverity → String

src/verso/Verso/Parser.lean

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -985,13 +985,13 @@ namespace Verso.Doc.Concrete
985985
open Verso.Parser
986986
open Lean Elab Term
987987

988-
public def stringToInlines [Monad m] [MonadError m] [MonadEnv m] [MonadQuotation m] (s : StrLit) : m (Array Syntax) :=
988+
public def stringToInlines [Monad m] [MonadError m] [MonadLog m] [MonadOptions m] [MonadEnv m] [MonadQuotation m] (s : StrLit) : m (Array Syntax) :=
989989
withRef s do
990-
return (← textLine.parseString s.getString).getArgs
990+
return (← textLine.parseString s).getArgs
991991

992992
open Lean Elab Term in
993-
public def stringToBlocks [Monad m] [MonadError m] [MonadEnv m] [MonadQuotation m] (s : StrLit) : m (Array Syntax) :=
993+
public def stringToBlocks [Monad m] [MonadError m] [MonadLog m] [MonadOptions m] [MonadEnv m] [MonadQuotation m] (s : StrLit) : m (Array Syntax) :=
994994
withRef s do
995-
return (← (blocks {}).parseString s.getString).getArgs
995+
return (← (blocks {}).parseString s).getArgs
996996

997997
end Verso.Doc.Concrete

0 commit comments

Comments
 (0)