All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gql.parser.ParserUtil.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2024 Valdemar Grange
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package gql.parser

import cats.parse._
import cats._
import cats.data._
import cats.implicits._

object ParserUtil {
  def showExpectation(e: Parser.Expectation): Eval[Chain[String]] = {
    import Parser.Expectation._
    Eval.defer {
      e match {
        case WithContext(contextStr, expect) =>
          showExpectation(expect).map(tl => Chain(s"context: ${contextStr}") ++ tl)
        case Fail(_) => Eval.now(Chain.empty)
        case FailWith(_, message) =>
          Eval.now(Chain(s"message $message"))
        case OneOfStr(_, strs) =>
          Eval.now(Chain(s"one of ${strs.map(x => s"\"$x\"").mkString(" | ")}"))
        case StartOfString(_) =>
          Eval.now(Chain("start of string"))
        case Length(_, expected, actual) =>
          Eval.now(Chain(s"length $expected, but found $actual"))
        case EndOfString(_, length) =>
          Eval.now(Chain(s"end of string but expected length $length"))
        case InRange(_, lower, upper) =>
          Eval.now(
            Chain(
              s"char in range $lower to $upper (code ${lower.toInt} to ${upper.toInt})"
            )
          )
        case ExpectedFailureAt(_, matched) =>
          Eval.now(Chain(s"expected $matched"))
      }
    }
  }

  def showExpectations(es: NonEmptyList[Parser.Expectation]): String =
    Chain.fromSeq(es.toList).flatTraverse(showExpectation).value.mkString_("\nin ")
  // es.traverse(showExpectation).value.mkString_("-" * 10)

  def errorMessage(data: String, e: Parser.Error): String = {
    import scala.io.AnsiColor
    val (msg, conflictingCharacter, ln) = showVirtualTextLine(data, e.failedAtOffset)
    val niceError = showExpectations(e.expected)

    AnsiColor.BLUE +
      s"failed at offset ${e.failedAtOffset} on line $ln with code ${conflictingCharacter.toInt}\n${niceError}\nfor document:\n$msg" +
      AnsiColor.RESET
  }

  def showVirtualTextLine(data: String, offset: Int) = {
    import scala.io.AnsiColor
    val (left, r0) = data.splitAt(offset)
    val right = r0.tail
    val column = left.reverseIterator.takeWhile(_ != '\n').size

    val ln = left.count(_ == '\n')

    val conflictingCharacter = r0.headOption.getOrElse('?')

    val virtualN = 3
    // may be negative
    val virtualLineStartChar = column - virtualN
    val virtualLineIndicators = math.min(virtualLineStartChar + virtualN, virtualN)

    val virtualErrorLine =
      (">" * virtualLineStartChar) + ("^" * (virtualLineIndicators + 1 + virtualN)) + s" line:$ln, column:${column}, offset:${offset}, character code code:${conflictingCharacter.toInt}"

    val green = AnsiColor.RESET + AnsiColor.GREEN

    val n = 400
    val leftChunk = left.takeRight(n)
    val rightChunk = right.take(n)

    val rightChunkThisline = rightChunk.takeWhile(_ != '\n')
    val rightChunkNextLines = rightChunk.dropWhile(_ != '\n')
    val rightChunks =
      green + rightChunkThisline + "\n" +
        AnsiColor.RED + virtualErrorLine +
        green + rightChunkNextLines + AnsiColor.RESET

    val conflictFmt = AnsiColor.RED_B + AnsiColor.BLACK + conflictingCharacter

    val chunk =
      green + leftChunk + conflictFmt + rightChunks

    val err = green + chunk
      .split("\n")
      .map(x => s"| $x")
      .mkString("\n")

    (err, conflictingCharacter, ln)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy