import Lean /-! # Parser error recovery This test is to make sure that the error recovery feature is working as intended. In particular, it checks that certain errors in the head of a declaration (e.g. an invalid name) don't prevent further parse errors from being reported. -/ open Lean Parser /-! ## Error recovery for Lean commands -/ def "foo" (x : Nat) : := 5 def 5 : Nat := 3 theorem yep : True := by trivial theorem () : Nat := 99 theorem 2.4 : Nat := ) theorem "nat" : Nat := ) theorem 3 : Nat := ) opaque ((( : Nat := ) axiom "nope" : 4 = ⟩ class inductive () where | "" : ) inductive 44: Type where | "" : 44 inductive 44 : Type where | "" : 44 inductive 44 (n : Nat) : Type where | "" : 44 inductive 44(n : Nat) : Type where | "" : 44 inductive 44| "" : 44 inductive !!! where | "" (x : Nat)) : Bogus #eval Foo.a inductive Item.{4, 5, 6, 7, a, "foo", b, c} : Nat where | foo : ) | bar : ) example := "" /-! ## Error recovery for custom syntax Adding recovery to Lean's term grammar doesn't work well with the primitive tools at hand (extensibility + token tables + backtracking are a real challenge), but for DSLs it can be useful. This tests that error recovery works for a term syntax extension. -/ def altParen : Parser := "{|" >> termParser (prec := 51) >> recover "|}" skip macro:50 e:altParen : term => pure ⟨e.raw[1]⟩ -- These terms show multiple errors due to recovery from missing |} tokens #eval ([ {|2 + 3] * {| 3 + |} -- Only one error here #eval ([ (2 + 3] * ( 3 + ) ) example := "" /-! ## Error recovery for custom commands This command can recover from many parse errors. Make sure that there's no arbitrary small limit by testing a few recoveries. -/ def nonws : Parser where fn := andthenFn (andthenFn (satisfyFn (fun c => !c.isWhitespace && c != '#')) (takeWhileFn (!·.isWhitespace))) whitespace def thing : Parser := recover ident nonws open Lean Elab Command in elab "#go " xs:thing* (Lean.Parser.eoi <|> "#stop") : command => do let xs := toString xs elabCommand <| ← `(#eval s!"hey {$(quote xs)}") -- Many errors due to recovery! #go hey 5 a g def 5 hey ( 99 ( #stop -- Even though the command had errors, this is still running as expected: def x := 5 #eval x