Skip to content

Commit

Permalink
feat: allow trailing comma in tuples, lists, and tactics (leanprover#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lenianiva authored and arthur-adjedj committed Nov 20, 2023
1 parent 0111afe commit 4489ede
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 40 deletions.
39 changes: 39 additions & 0 deletions src/Init/Data/List/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,48 @@ Author: Leonardo de Moura
prelude
import Init.SimpLemmas
import Init.Data.Nat.Basic
import Init.Data.Nat.Div
set_option linter.missingDocs true -- keep it documented
open Decidable List

/--
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
list literals.
For lists of length at least 64, an alternative desugaring strategy is used
which uses let bindings as intermediates as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
Note that this changes the order of evaluation, although it should not be observable
unless you use side effecting operations like `dbg_trace`.
-/
syntax "[" withoutPosition(term,*,?) "]" : term

/--
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
It uses binary partitioning to construct a tree of intermediate let bindings as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
-/
syntax "%[" withoutPosition(term,*,? " | " term) "]" : term

namespace Lean

macro_rules
| `([ $elems,* ]) => do
-- NOTE: we do not have `TSepArray.getElems` yet at this point
let rec expandListLit (i : Nat) (skip : Bool) (result : TSyntax `term) : MacroM Syntax := do
match i, skip with
| 0, _ => pure result
| i+1, true => expandListLit i false result
| i+1, false => expandListLit i true (← ``(List.cons $(⟨elems.elemsAndSeps.get! i⟩) $result))
let size := elems.elemsAndSeps.size
if size < 64 then
expandListLit size (size % 2 == 0) (← ``(List.nil))
else
`(%[ $elems,* | List.nil ])
end Lean

universe u v w

variable {α : Type u} {β : Type v} {γ : Type w}
Expand Down
34 changes: 0 additions & 34 deletions src/Init/Notation.lean
Original file line number Diff line number Diff line change
Expand Up @@ -461,42 +461,8 @@ expected type is known. So, `without_expected_type` is not effective in this cas
-/
macro "without_expected_type " x:term : term => `(let aux := $x; aux)

/--
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
list literals.
For lists of length at least 64, an alternative desugaring strategy is used
which uses let bindings as intermediates as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
Note that this changes the order of evaluation, although it should not be observable
unless you use side effecting operations like `dbg_trace`.
-/
syntax "[" withoutPosition(term,*) "]" : term

/--
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
It uses binary partitioning to construct a tree of intermediate let bindings as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
-/
syntax "%[" withoutPosition(term,* " | " term) "]" : term

namespace Lean

macro_rules
| `([ $elems,* ]) => do
-- NOTE: we do not have `TSepArray.getElems` yet at this point
let rec expandListLit (i : Nat) (skip : Bool) (result : TSyntax `term) : MacroM Syntax := do
match i, skip with
| 0, _ => pure result
| i+1, true => expandListLit i false result
| i+1, false => expandListLit i true (← ``(List.cons $(⟨elems.elemsAndSeps.get! i⟩) $result))
if elems.elemsAndSeps.size < 64 then
expandListLit elems.elemsAndSeps.size false (← ``(List.nil))
else
`(%[ $elems,* | List.nil ])

/--
Category for carrying raw syntax trees between macros; any content is printed as is by the pretty printer.
The only accepted parser for this category is an antiquotation.
Expand Down
6 changes: 3 additions & 3 deletions src/Init/Tactics.lean
Original file line number Diff line number Diff line change
Expand Up @@ -435,22 +435,22 @@ non-dependent hypotheses. It has many variants:
other hypotheses.
-/
syntax (name := simp) "simp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*) "]")? (location)? : tactic
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*,?) "]")? (location)? : tactic
/--
`simp_all` is a stronger version of `simp [*] at *` where the hypotheses and target
are simplified multiple times until no simplification is applicable.
Only non-dependent propositional hypotheses are considered.
-/
syntax (name := simpAll) "simp_all" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? : tactic
(" [" withoutPosition((simpErase <|> simpLemma),*,?) "]")? : tactic

/--
The `dsimp` tactic is the definitional simplifier. It is similar to `simp` but only
applies theorems that hold by reflexivity. Thus, the result is guaranteed to be
definitionally equal to the input.
-/
syntax (name := dsimp) "dsimp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? (location)? : tactic
(" [" withoutPosition((simpErase <|> simpLemma),*,?) "]")? (location)? : tactic

/--
`delta id1 id2 ...` delta-expands the definitions `id1`, `id2`, ....
Expand Down
4 changes: 2 additions & 2 deletions src/Lean/Parser/Term.lean
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ do not yield the right result.
"(" >> (withoutPosition (withoutForbidden (termParser >> " :" >> optional (ppSpace >> termParser)))) >> ")"
/-- Tuple notation; `()` is short for `Unit.unit`, `(a, b, c)` for `Prod.mk a (Prod.mk b c)`, etc. -/
@[builtin_term_parser] def tuple := leading_parser
"(" >> optional (withoutPosition (withoutForbidden (termParser >> ", " >> sepBy1 termParser ", "))) >> ")"
"(" >> optional (withoutPosition (withoutForbidden (termParser >> ", " >> sepBy1 termParser ", " (allowTrailingSep := true)))) >> ")"
/--
Parentheses, used for grouping expressions (e.g., `a * (b + c)`).
Can also be used for creating simple functions when combined with `·`. Here are some examples:
Expand All @@ -184,7 +184,7 @@ are turned into a new anonymous constructor application. For example,
`⟨a, b, c⟩ : α × (β × γ)` is equivalent to `⟨a, ⟨b, c⟩⟩`.
-/
@[builtin_term_parser] def anonymousCtor := leading_parser
"⟨" >> withoutPosition (withoutForbidden (sepBy termParser ", ")) >> "⟩"
"⟨" >> withoutPosition (withoutForbidden (sepBy termParser ", " (allowTrailingSep := true))) >> "⟩"
def optIdent : Parser :=
optional (atomic (ident >> " : "))
def fromTerm := leading_parser
Expand Down
6 changes: 6 additions & 0 deletions tests/compiler/tuple.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

@[noinline] def f (a : Nat × Nat × Nat) : Nat:=
a.fst + a.snd.fst + a.snd.snd

def main : IO Unit := do
IO.println (f (1, 2, 3,))
1 change: 1 addition & 0 deletions tests/compiler/tuple.lean.expected.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6
2 changes: 1 addition & 1 deletion tests/lean/StxQuot.lean
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Lean.Syntax
#eval run $ let id := miss; `($id + 1)
end Lean.Syntax
#eval run `(1 + 1)
#eval run `([x,])
#eval run `([x+])
#eval run $ `(fun a => a) >>= pure
#eval run $ `(def foo := 1)
#eval run $ `(def foo := 1 def bar := 2)
Expand Down
17 changes: 17 additions & 0 deletions tests/lean/trailingComma.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Lean
open Lean

#eval [1,2,3,]
#eval (2,3,)
#eval [1,2,3,,] -- Errors, double trailing comma
#eval (4,5,,,) -- ditto

axiom zeroAdd (x : Nat) : 0 + x = x
theorem rewrite_comma (x y z: Nat) (h₁ : 0 + x = y) (h₂ : 0 + y = z) : x = z := by
rewrite [zeroAdd,] at *;
subst x;
subst y;
exact rfl

theorem simp_comma (x: Nat) : x = x := by
simp [zeroAdd,]
4 changes: 4 additions & 0 deletions tests/lean/trailingComma.lean.expected.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[1, 2, 3]
(2, 3)
trailingComma.lean:6:13-6:14: error: unexpected token ','; expected ']'
trailingComma.lean:7:11-7:12: error: unexpected token ','; expected ')'

0 comments on commit 4489ede

Please sign in to comment.