Zulip Chat Archive

Stream: lean4

Topic: Incorrect indents when processing Syntax


Eric Wieser (Mar 25 2024 at 09:19):

What's going on here?

import Lean

open Lean Meta

partial def stripWithReducible {m} [Monad m] [MonadRef m] [MonadQuotation m] (s : Syntax) : m Syntax :=
  s.replaceM fun
    | `(tactic| with_reducible $tacs:tacticSeq) => do
      let tacs :=  stripWithReducible tacs
      return some <|  `(tactic| $tacs)
    | _ => return none

/--
info: by
    let x✝ := "hello"
    let y✝ := "world"
  let z✝ := "oh no"
  exact "where is my indent"
-/
#guard_msgs in
run_cmd Elab.Command.liftTermElabM do
  let t  stripWithReducible <| ←`(by
    with_reducible
      let x := "hello"
      let y := "world"
    with_reducible
      let z := "oh no"
    exact "where is my indent")
  Lean.logInfo t
  • The resulting syntax has invalid indentation
  • The guard_msgss is complaining despite the fact the output is exactly what it claims to expect.

Damiano Testa (Mar 25 2024 at 09:37):

The Syntax for t contains twice the nodes Lean.Parser.Tactic.tacticSeq Lean.Parser.Tactic.tacticSeq1Indented, since, I think, your match only removed the node corresponding to with_reducible, but not also the formatting.

Damiano Testa (Mar 25 2024 at 09:38):

This is the syntax tree for t (I hope that this is fairly self-explanatory):

inspect: 'by
    let x := "hello"
    let y := "world"
  let z := "oh no"
  exact "where is my indent"'

node Lean.Parser.Term.byTactic, synthetic: false
|-atom synthetic: false-- 'by'
|-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |   |   |   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (x._@.Mathlib.Tactic.mwe_linter._hyg.393,x)
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"hello"'
|   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (y._@.Mathlib.Tactic.mwe_linter._hyg.393,y)
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"world"'
|   |   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |   |   |   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (z._@.Mathlib.Tactic.mwe_linter._hyg.393,z)
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"oh no"'
|   |   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.exact, synthetic: false
|   |   |   |   |-atom synthetic: false-- 'exact'
|   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |-atom synthetic: false-- '"where is my indent"'

Eric Wieser (Mar 25 2024 at 09:49):

I figured it might be something like this; so I guess my question is now "are there any helper functions to disassemble this in the right way?"

Damiano Testa (Mar 25 2024 at 09:50):

Actually, I am investigating where the extra indent is inserted, but it seems that it is not at the return some <| ← `(tactic| $tacs) step, which puzzles me.

Eric Wieser (Mar 25 2024 at 09:51):

I think the indent might just be a pretty-printer bug; Aesop has a workaround somewhere that refers to indents and tacticSeq

Eric Wieser (Mar 25 2024 at 09:51):

But perhaps the Syntax I've assembled is illegal so the pretty-printer behavior is not specified.

Damiano Testa (Mar 25 2024 at 09:52):

Ah, you are saying that it might be last logInfo step that does that?

Damiano Testa (Mar 25 2024 at 09:53):

Btw, I noticed that most syntax trees are "padded" with null nodes. I wonder if that is what you are missing.

Damiano Testa (Mar 25 2024 at 09:55):

In case you are curious, this is what the syntax tree for the syntax that you wrote before stripping with reducible:

inspect: 'by
  with_reducible
    let x := "hello"
    let y := "world"
  with_reducible let z := "oh no"
  exact "where is my indent"'

node Lean.Parser.Term.byTactic, synthetic: false
|-atom synthetic: false-- 'by'
|-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.withReducible, synthetic: false
|   |   |   |   |-atom synthetic: false-- 'with_reducible'
|   |   |   |   |-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |   |   |   |   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (x._@.Mathlib.Tactic.mwe_linter._hyg.238,x)
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"hello"'
|   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (y._@.Mathlib.Tactic.mwe_linter._hyg.238,y)
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"world"'
|   |   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.withReducible, synthetic: false
|   |   |   |   |-atom synthetic: false-- 'with_reducible'
|   |   |   |   |-node Lean.Parser.Tactic.tacticSeq, synthetic: false
|   |   |   |   |   |-node Lean.Parser.Tactic.tacticSeq1Indented, synthetic: false
|   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |-node Lean.Parser.Tactic.tacticLet_, synthetic: false
|   |   |   |   |   |   |   |   |-atom synthetic: false-- 'let'
|   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |-node Lean.Parser.Term.letIdDecl, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-ident synthetic: false-- (z._@.Mathlib.Tactic.mwe_linter._hyg.238,z)
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-node null, synthetic: false
|   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- ':='
|   |   |   |   |   |   |   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |   |   |   |   |   |   |-atom synthetic: false-- '"oh no"'
|   |   |   |-node null, synthetic: false
|   |   |   |-node Lean.Parser.Tactic.exact, synthetic: false
|   |   |   |   |-atom synthetic: false-- 'exact'
|   |   |   |   |-node str, synthetic: false
|   |   |   |   |   |-atom synthetic: false-- '"where is my indent"'

Mario Carneiro (Mar 25 2024 at 21:38):

@Eric Wieser said:

But perhaps the Syntax I've assembled is illegal so the pretty-printer behavior is not specified.

yes, you constructed invalid syntax here because $tacs is a tacticSeq and you used it as a tactic

Mario Carneiro (Mar 25 2024 at 21:41):

I think there is a special hack in the `(tactic| ...) syntax quotation to allow this to work even though it is not syntactically correct, because several tactic-taking functions like elabTactic also work on tacticSeq. However this is not one of them - you can't just put multiple tacticSeqs in a single tacticSeq without some kind of grouping like (...)

Mario Carneiro (Mar 25 2024 at 21:41):

What exactly were you expecting to get?

Eric Wieser (Mar 25 2024 at 21:52):

Mario Carneiro said:

What exactly were you expecting to get?

Whoops, I should have started with that. I wanted to end up with

by
  let x := "hello"
  let y := "world"
  let z := "oh no"
  exact "where is my indent"

Eric Wieser (Mar 25 2024 at 21:53):

Mario Carneiro said:

I think there is a special hack in the `(tactic| ...) syntax quotation to allow this to work even though it is not syntactically correct, because several tactic-taking functions like elabTactic also work on tacticSeq. However this is not one of them - you can't just put multiple tacticSeqs in a single tacticSeq without some kind of grouping like (...)

Ouch; I was operating on the assumption that `(tactic| ...) would give an error if I did anything illegal.

Mario Carneiro (Mar 25 2024 at 21:57):

oh, also you kinda lied to the typechecker with this line

      let tacs :=  stripWithReducible tacs

which is converting a Syntax to TSyntax `tactic even though it's actually a TSyntax `tacticSeq

Eric Wieser (Mar 25 2024 at 21:58):

Ah, good point

Mario Carneiro (Mar 25 2024 at 22:02):

I'm not observing your second issue though, #guard_msgs is silent on the original example

Eric Wieser (Mar 25 2024 at 22:04):

I think this is a caching issue that I seem to keep running into; you can end up with info messages stuck in vscode from a previous compilation

Kyle Miller (Mar 25 2024 at 22:11):

This isn't relevant to this thread, but, speaking of caching issues, here is a fun syntax highlighting one I had the other day until I restarted Lean:

image.png

Eric Wieser (Mar 25 2024 at 22:12):

(I ended up just hacking with the raw syntax objects to solve my problem, so I am ok with this thread being derailed)


Last updated: May 02 2025 at 03:31 UTC