feat: relax eager antiquotation parsing

This commit is contained in:
Sebastian Ullrich 2022-06-30 11:22:01 +02:00
parent 9e9786203f
commit b1a9c58d43

View file

@ -485,9 +485,14 @@ def mergeOrElseErrors (s : ParserState) (error1 : Error) (iniPos : String.Pos) (
else s
| other => other
-- When `mergeAntiquots` is true, if `p` parses an antiquotation, we try `q` as well and return a choice node if they
-- both return antiquotations
def orelseFnCore (p q : ParserFn) (mergeAntiquots : Bool) : ParserFn := fun c s => Id.run do
-- When `p` in `p <|> q` parses exactly one antiquotation, ...
inductive OrElseOnAntiquotBehavior where
| acceptLhs -- return it
| takeLongest -- return result of `q` instead if it made more progress
| merge -- ... and create choice node if both made the same progress
deriving BEq
def orelseFnCore (p q : ParserFn) (antiquotBehavior := OrElseOnAntiquotBehavior.merge) : ParserFn := fun c s => Id.run do
let s0 := s
let iniSz := s.stackSize
let iniPos := s.pos
@ -500,17 +505,24 @@ def orelseFnCore (p q : ParserFn) (mergeAntiquots : Bool) : ParserFn := fun c s
s
| none =>
let back := s.stxStack.back
if mergeAntiquots && back.isAntiquots then
if antiquotBehavior != .acceptLhs && s.stackSize == iniSz + 1 && back.isAntiquots then
let s' := q c s0
if !s'.hasError && s'.stxStack.back.isAntiquot then
if back.isOfKind choiceKind then
s := { s with stxStack := s.stxStack.pop ++ back.getArgs }
s := s.pushSyntax s'.stxStack.back
s := s.mkNode choiceKind iniSz
if !s'.hasError then
-- If `q` made more progress than `p`, we prefer its result.
-- Thus `(structInstField| $id := $val) is interpreted as
-- `(structInstField| $id:ident := $val:term), not
-- `(structInstField| $id:structInstField <ERROR: expected ')'>.
if s'.pos > s.pos then
return s'
else if antiquotBehavior == .merge && s'.stackSize == iniSz + 1 && s'.stxStack.back.isAntiquot then
if back.isOfKind choiceKind then
s := { s with stxStack := s.stxStack.pop ++ back.getArgs }
s := s.pushSyntax s'.stxStack.back
s := s.mkNode choiceKind iniSz
s
@[inline] def orelseFn (p q : ParserFn) : ParserFn :=
orelseFnCore (mergeAntiquots := true) p q
orelseFnCore p q
@[noinline] def orelseInfo (p q : ParserInfo) : ParserInfo := {
collectTokens := p.collectTokens ∘ q.collectTokens,
@ -1740,11 +1752,14 @@ def mkAntiquot (name : String) (kind : SyntaxNodeKind) (anonymous := true) (isPs
checkNoWsBefore "no space before spliced term" >> antiquotExpr >>
nameP
@[inline] def withAntiquotFn (antiquotP p : ParserFn) : ParserFn := fun c s =>
@[inline] def withAntiquotFn (antiquotP p : ParserFn) (isCatAntiquot := false) : ParserFn := fun c s =>
-- fast check that is false in most cases
if c.input.get s.pos == '$' then
-- do not allow antiquotation choice nodes here as `antiquotP` is the strictly more general antiquotation than any in `p`
orelseFnCore (mergeAntiquots := false) antiquotP p c s
-- Do not allow antiquotation choice nodes here as `antiquotP` is the strictly more general
-- antiquotation than any in `p`.
-- If it is a category antiquotation, do not backtrack into the category at all as that would
-- run *all* parsers of the category, and trailing parsers will later be applied anyway.
orelseFnCore (antiquotBehavior := if isCatAntiquot then .acceptLhs else .takeLongest) antiquotP p c s
else
p c s
@ -1833,7 +1848,7 @@ def leadingParserAux (kind : Name) (tables : PrattParsingTables) (behavior : Lea
mkResult s iniSz
@[inline] def leadingParser (kind : Name) (tables : PrattParsingTables) (behavior : LeadingIdentBehavior) (antiquotParser : ParserFn) : ParserFn :=
withAntiquotFn antiquotParser (leadingParserAux kind tables behavior)
withAntiquotFn (isCatAntiquot := true) antiquotParser (leadingParserAux kind tables behavior)
def trailingLoopStep (tables : PrattParsingTables) (left : Syntax) (ps : List (Parser × Nat)) : ParserFn := fun c s =>
longestMatchFn left (ps ++ tables.trailingParsers) c s