Skip to content

Commit

Permalink
Improved position API (#146)
Browse files Browse the repository at this point in the history
* Added positions object

* deprecated old position stuff
  • Loading branch information
j-mie6 authored Jan 19, 2023
1 parent e9f5a11 commit 68db738
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 67 deletions.
65 changes: 9 additions & 56 deletions parsley/shared/src/main/scala/parsley/Parsley.scala
Original file line number Diff line number Diff line change
Expand Up @@ -944,13 +944,6 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* useful; in particular, `pure` and `unit` can be put to good use in injecting results into a parser
* without needing to consume anything, or mapping another parser.
*
* @groupprio pos 10
* @groupname pos Position-Tracking Parsers
* @groupdesc pos
* These parsers provide a way to extract position information during a parse. This can be important
* for when the final result of the parser needs to encode position information for later consumption:
* this is particularly useful for abstract syntax trees.
*
* @groupprio monad 100
* @groupname monad Expensive Sequencing Combinators
* @groupdesc monad
Expand Down Expand Up @@ -1218,66 +1211,26 @@ object Parsley {
* @group basic
*/
val unit: Parsley[Unit] = pure(())
// $COVERAGE-OFF$
/** This parser returns the current line number of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current line number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.Parsley.line, parsley.character.char
* scala> line.parse("")
* val res0 = Success(1)
* scala> (char('a') *> line).parse("a")
* val res0 = Success(1)
* scala> (char('\n') *> line).parse("\n")
* val res0 = Success(2)
* }}}
*
* @return a parser that returns the line number the parser is currently at.
* @group pos
* @deprecated Moved to [[position.line `position.line`]], due for removal in 5.0.0
*/
@deprecated("Position parsing functionality was moved to `parsley.position`; use `position.line` instead as this will be removed in 5.0.0", "4.2.0")
def line: Parsley[Int] = position.line
/** This parser returns the current column number of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current column number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.Parsley.col, parsley.character.char
* scala> col.parse("")
* val res0 = Success(1)
* scala> (char('a') *> col).parse("a")
* val res0 = Success(2)
* scala> (char('\n') *> col).parse("\n")
* val res0 = Success(1)
* }}}
*
* @return a parser that returns the column number the parser is currently at.
* @note in the presence of wide unicode characters, the value returned may be inaccurate.
* @group pos
* @note in the presence of wide unicode characters, the column value returned may be inaccurate.
* @deprecated Moved to [[position.col `position.col`]], due for removal in 5.0.0
*/
@deprecated("Position parsing functionality was moved to `parsley.position`; use `position.line` instead as this will be removed in 5.0.0", "4.2.0")
def col: Parsley[Int] = position.col
/** This parser returns the current line and column numbers of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
* the current line and column number will always be successfully returned. It has no other
* effect on the state of the parser.
*
* @example {{{
* scala> import parsley.Parsley.pos, parsley.character.char
* scala> pos.parse("")
* val res0 = Success((1, 1))
* scala> (char('a') *> pos).parse("a")
* val res0 = Success((1, 2))
* scala> (char('\n') *> pos).parse("\n")
* val res0 = Success((2, 1))
* }}}
*
* @return a parser that returns the line and column number the parser is currently at.
* @note in the presence of wide unicode characters, the column value returned may be inaccurate.
* @group pos
* @deprecated Moved to [[position.pos `position.pos`]], due for removal in 5.0.0
*/
@deprecated("Position parsing functionality was moved to `parsley.position`; use `position.line` instead as this will be removed in 5.0.0", "4.2.0")
def pos: Parsley[(Int, Int)] = position.pos
// $COVERAGE-ON$
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import parsley.XCompat.unused
import parsley.character.item
import parsley.combinator.{choice, eof, option, sequence, someUntil}
import parsley.errors.{ErrorBuilder, Token, TokenSpan}
import parsley.position

// Turn coverage off, because the tests have their own error builder
// TODO: We might want to test this on its own though
Expand Down Expand Up @@ -58,7 +59,7 @@ trait LexToken { this: ErrorBuilder[_] =>

// this parser cannot fail
private lazy val makeParser: Parsley[Either[String, List[(String, (Int, Int))]]] = {
val toks = tokens.map(p => attempt(p <~> Parsley.pos))
val toks = tokens.map(p => attempt(p <~> position.pos))
// TODO: I think this can be improved to delay raw token till after we have established
// no valid tokens: this would be slightly more efficient.
val rawTok = lookAhead(someUntil(item, eof <|> choice(toks: _*))).map(_.mkString)
Expand Down
17 changes: 9 additions & 8 deletions parsley/shared/src/main/scala/parsley/position.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import parsley.implicits.zipped.Zipped3

import parsley.internal.deepembedding.singletons

// TODO: In future, the contents of this object will be made public, and the old versions
// will be deprecated for removal in 5.0.0
private [parsley] object position {
/** TODO: Document
* These parsers provide a way to extract position information during a parse. This can be important
* for when the final result of the parser needs to encode position information for later consumption:
* this is particularly useful for abstract syntax trees.
*/
object position {
/** This parser returns the current line number of the input without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
Expand Down Expand Up @@ -75,12 +78,10 @@ private [parsley] object position {

// this is subject to change at the slightest notice, do NOT expose
private [parsley] val internalOffset: Parsley[Int] = new Parsley(singletons.Offset)
// IMPORTANT: this is NOT to be released until a Int/Long stance has been taken
// for offset in the deepest internals
// We could use `BigInt` as an arbiter here, and just declare it's expensive?
private [parsley] val offset: Parsley[BigInt] = internalOffset.map(BigInt(_))
/** TODO: Document */
val offset: Parsley[Int] = internalOffset

def spanWith[A, S](end: Parsley[S])(p: Parsley[A]): Parsley[(S, A, S)] = (end, p, end).zipped
private [parsley] def spanWith[A, S](end: Parsley[S])(p: Parsley[A]): Parsley[(S, A, S)] = (end, p, end).zipped
// this is subject to change at the slightest notice, do NOT expose
private [parsley] def internalOffsetSpan[A](p: Parsley[A]): Parsley[(Int, A, Int)] = spanWith(internalOffset)(p)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import parsley.character.digit
import parsley.implicits.character.{charLift, stringLift}
import parsley.expr.{chain, infix, mixed}
import parsley.expr.{precedence, Ops, GOps, SOps, InfixL, InfixR, Prefix, Postfix, InfixN, Atoms}
import parsley.Parsley._
import parsley.position._
import parsley.genericbridges._

class ExpressionParserTests extends ParsleyTest {
Expand Down
3 changes: 2 additions & 1 deletion parsley/shared/src/test/scala/parsley/StringTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Predef.{ArrowAssoc => _, _}

import parsley.character.{letter, string, strings, stringOfMany, stringOfSome}
import parsley.implicits.character.{charLift, stringLift}
import parsley.Parsley._
import parsley.Parsley.{pos => _, _}
import parsley.position.pos

class StringTests extends ParsleyTest {
private def stringPositionCheck(initialCol: Int, str: String) = {
Expand Down

0 comments on commit 68db738

Please sign in to comment.