From 73f1e209516607d184b1dbccafcd570d00b26fd3 Mon Sep 17 00:00:00 2001 From: Jamie Willis Date: Sat, 31 Dec 2022 17:42:45 +0000 Subject: [PATCH] Fixed missing wider caret for unexpectedless trivial errors (#137) * Persisted caret info from NoItem forward into TrivialError by using Either instead of Option * Updated readme --- README.md | 2 +- .../main/scala/parsley/internal/errors/ParseError.scala | 6 +++--- .../parsley/internal/machine/errors/DefuncBuilders.scala | 8 ++++---- .../internal/machine/instructions/DebugInstrs.scala | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1dc84af28..c88413586 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ _An exception to this policy is made for any version `3.x.y`, which reaches EoL | `3.3.0` | January 7th 2022 | EoL reached | | `4.0.0` | November 30th 2022 | Enjoying indefinite support | -## Bug Reports [![Percentage of issues still open](https://isitmaintained.com/badge/open/j-mie6/Parsley.svg)](https://isitmaintained.com/project/j-mie6/Parsley "Percentage of issues still open") [![Maintainability](https://img.shields.io/codeclimate/maintainability/j-mie6/Parsley)](https://codeclimate.com/github/j-mie6/Parsley) [![Test Coverage](https://img.shields.io/codeclimate/coverage-letter/j-mie6/Parsley)](https://codeclimate.com/github/j-mie6/Parsley) +## Bug Reports [![Percentage of issues still open](https://isitmaintained.com/badge/open/j-mie6/Parsley.svg)](https://isitmaintained.com/project/j-mie6/Parsley "Percentage of issues still open") [![Maintainability](https://img.shields.io/codeclimate/maintainability/j-mie6/parsley)](https://codeclimate.com/github/j-mie6/Parsley) [![Test Coverage](https://img.shields.io/codeclimate/coverage-letter/j-mie6/parsley)](https://codeclimate.com/github/j-mie6/Parsley) If you encounter a bug when using Parsley, try and minimise the example of the parser (and the input) that triggers the bug. If possible, make a self contained example: this will help to identify the issue without too much issue. diff --git a/parsley/shared/src/main/scala/parsley/internal/errors/ParseError.scala b/parsley/shared/src/main/scala/parsley/internal/errors/ParseError.scala index 411efd97b..9f97ac3e0 100644 --- a/parsley/shared/src/main/scala/parsley/internal/errors/ParseError.scala +++ b/parsley/shared/src/main/scala/parsley/internal/errors/ParseError.scala @@ -22,14 +22,14 @@ private [internal] sealed trait ParseError { } // The reasons here are lightweight, two errors can merge their messages, but messages do not get converted to hints private [internal] case class TrivialError(offset: Int, line: Int, col: Int, - unexpected: Option[UnexpectItem], expecteds: Set[ExpectItem], reasons: Set[String], lexicalError: Boolean) + unexpected: Either[Int, UnexpectItem], expecteds: Set[ExpectItem], reasons: Set[String], lexicalError: Boolean) extends ParseError { def format(line: String, beforeLines: List[String], afterLines: List[String], caret: Int)(implicit builder: ErrorBuilder[_]): builder.ErrorInfoLines = { val unexpectedTok = unexpected.map(_.formatUnexpect(lexicalError)) // FIXME: This should probably use the number of codepoints and not length - val caretSize = unexpectedTok.fold(1)(_._2.toCaretLength(this.col, line.length, afterLines.map(_.length))) + val caretSize = unexpectedTok.fold(identity[Int], _._2.toCaretLength(this.col, line.length, afterLines.map(_.length))) builder.vanillaError( - builder.unexpected(unexpectedTok.map(_._1)), + builder.unexpected(unexpectedTok.toOption.map(_._1)), builder.expected(builder.combineExpectedItems(expecteds.map(_.formatExpect))), builder.combineMessages(reasons.map(builder.reason(_)).toSeq), builder.lineInfo(line, beforeLines, afterLines, caret, caretSize)) diff --git a/parsley/shared/src/main/scala/parsley/internal/machine/errors/DefuncBuilders.scala b/parsley/shared/src/main/scala/parsley/internal/machine/errors/DefuncBuilders.scala index 37710b09d..48394568e 100644 --- a/parsley/shared/src/main/scala/parsley/internal/machine/errors/DefuncBuilders.scala +++ b/parsley/shared/src/main/scala/parsley/internal/machine/errors/DefuncBuilders.scala @@ -110,28 +110,28 @@ private [errors] object TrivialErrorBuilder { protected [TrivialErrorBuilder] def pickRaw(other: Raw): BuilderUnexpectItem protected [TrivialErrorBuilder] def pickOther(other: Other): Other protected [TrivialErrorBuilder] def pickNoItem(other: NoItem): BuilderUnexpectItem - def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Option[UnexpectItem] + def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Either[Int, UnexpectItem] } private [TrivialErrorBuilder] final class Raw(val size: Int) extends BuilderUnexpectItem { final def pickHigher(other: BuilderUnexpectItem): BuilderUnexpectItem = other.pickRaw(this) final override def pickRaw(other: Raw): Raw = if (this.size > other.size) this else other final override def pickOther(other: Other): Other = other final override def pickNoItem(other: NoItem): Raw = this - def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Option[UnexpectItem] = Some(builder(offset, size)) + def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Either[Int, UnexpectItem] = Right(builder(offset, size)) } private [TrivialErrorBuilder] final class Other(val underlying: UnexpectItem) extends BuilderUnexpectItem { final def pickHigher(other: BuilderUnexpectItem): BuilderUnexpectItem = other.pickOther(this) final override def pickRaw(other: Raw): Other = this final override def pickOther(other: Other): Other = if (this.underlying.higherPriority(other.underlying)) this else other final override def pickNoItem(other: NoItem): Other = this - def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Option[UnexpectItem] = Some(underlying) + def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Either[Int, UnexpectItem] = Right(underlying) } private [TrivialErrorBuilder] class NoItem(val width: Int) extends BuilderUnexpectItem { final def pickHigher(other: BuilderUnexpectItem): BuilderUnexpectItem = other.pickNoItem(this) final override def pickRaw(other: Raw): Raw = other final override def pickOther(other: Other): Other = other final override def pickNoItem(other: NoItem): NoItem = if (this.width > other.width) this else other - def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Option[UnexpectItem] = None + def toErrorItem(offset: Int)(implicit builder: ErrorItemBuilder): Either[Int, UnexpectItem] = Left(width) } } diff --git a/parsley/shared/src/main/scala/parsley/internal/machine/instructions/DebugInstrs.scala b/parsley/shared/src/main/scala/parsley/internal/machine/instructions/DebugInstrs.scala index b6c66d76b..c4310b4cc 100644 --- a/parsley/shared/src/main/scala/parsley/internal/machine/instructions/DebugInstrs.scala +++ b/parsley/shared/src/main/scala/parsley/internal/machine/instructions/DebugInstrs.scala @@ -221,7 +221,7 @@ private [instructions] object LogErrEnd { case FancyError(offset, line, col, msgs, _, _) => s"generated specialised error (offset $offset, line $line, col $col) {" +: msgs :+ "}" case TrivialError(offset, line, col, unexpected, expecteds, reasons, lexicalError) => Seq(s"generated vanilla error (offset $offset, line $line, col $col) {", - s" unexpected item = ${unexpected.fold("missing")(_.formatUnexpect(lexicalError)._1.toString)}", + s" unexpected item = ${unexpected.fold(_ => "missing", _.formatUnexpect(lexicalError)._1.toString)}", s" expected item(s) = ${expecteds.map(_.formatExpect)}", s" reasons =${if (reasons.isEmpty) " no reasons given" else ""}") ++ reasons.map(" " + _) :+