<commit_before>package fpinscala.parsing

import java.util.regex._
import scala.util.matching.Regex
import fpinscala.testing._
import fpinscala.testing.Prop._

trait Parsers[Parser[+_]] { self => // so inner classes may call methods of trait
  def run[A](p: Parser[A])(input: String): Either[ParseError,A]

  implicit def string(s: String): Parser[String] // implicit conversion from string to Parser[String]
  implicit def operators[A](p: Parser[A]) = ParserOps[A](p) // implicit conversion to enable infix ParserOps[A]
  implicit def asStringParser[A](a: A)(implicit f: A => Parser[String]): ParserOps[String] = ParserOps(f(a))

  def or[A](s1: Parser[A], s2: Parser[A]): Parser[A]
  def map[A,B](p: Parser[A])(f: A => B): Parser[B]
  def product[A,B](p1: Parser[A], p2: => Parser[B]): Parser[(A,B)]
  def map2[A,B,C](p1: Parser[A], p2: => Parser[B])(f: (A,B) => C): Parser[C] =
    (p1 ** p2) map(f.tupled)
  def slice[A](p: Parser[A]): Parser[String]

  def listOfN[A](n: Int, p: Parser[A]): Parser[List[A]] =
    if (n == 0) succeed(List[A]())
    else map2(p, listOfN(n-1,p))(_ :: _)
  def many[A](p: Parser[A]): Parser[List[A]] =
    map2(p, many(p))(_ :: _) | succeed(List[A]())
  def many1[A](p: Parser[A]): Parser[List[A]] =
    map2(p, many(p))(_ :: _)

  // parses a single character
  def char(c: Char): Parser[Char] = string(c.toString) map (_.charAt(0))

  // unit, lifts a value A into a Parser[A]
  def succeed[A](a: A): Parser[A] = string("") map (_ => a)


  case class ParserOps[A](p: Parser[A]) {
    def |[B>:A](p2: Parser[B]): Parser[B] = self.or(p,p2)
    def or[B>:A](p2: => Parser[B]): Parser[B] = self.or(p,p2)
    def many: Parser[List[A]] = self.many(p)
    def map[B](f: A => B): Parser[B] = self.map(p)(f)
    def **[B](p2: Parser[B]): Parser[(A,B)] = self.product(p, p2)
    def product[B](p2: Parser[B]): Parser[(A,B)] = self.product(p, p2)
  }<commit_msg>Refactor primitive and derived combinators<commit_after>package fpinscala.parsing

import java.util.regex._
import scala.util.matching.Regex
import fpinscala.testing._
import fpinscala.testing.Prop._

trait Parsers[Parser[+_]] { self => // so inner classes may call methods of trait
  def run[A](p: Parser[A])(input: String): Either[ParseError,A]

  /*
   Primitive Combinators
   */
  implicit def string(s: String): Parser[String] // implicit conversion from string to Parser[String]
  implicit def asStringParser[A](a: A)(implicit f: A => Parser[String]): ParserOps[String] = ParserOps(f(a))

  def slice[A](p: Parser[A]): Parser[String]

  // unit, lifts a value A into a Parser[A]
  def succeed[A](a: A): Parser[A] = string("") map (_ => a)

  def map[A,B](p: Parser[A])(f: A => B): Parser[B]

  def product[A,B](p1: Parser[A], p2: => Parser[B]): Parser[(A,B)]

  // Left biased: evaluates s1 before s2 (no longer associative!)
  def or[A](s1: Parser[A], s2: => Parser[A]): Parser[A]

  /*
   Derived Combinators
   */

  // Non strictness:
  // 1. If the first parser fails, the second won't even be run!
  // 2. Recursive arguments (e.g. map2(p, many(p))) won't infinite loop
  def map2[A,B,C](p1: Parser[A], p2: => Parser[B])(f: (A,B) => C): Parser[C] =
    (p1 ** p2) map(f.tupled)

  def listOfN[A](n: Int, p: Parser[A]): Parser[List[A]] =
    if (n == 0) succeed(List[A]())
    else map2(p, listOfN(n-1,p))(_ :: _)
  def many[A](p: Parser[A]): Parser[List[A]] =
    map2(p, many(p))(_ :: _) | succeed(List[A]())
  def many1[A](p: Parser[A]): Parser[List[A]] =
    map2(p, many(p))(_ :: _)

  // parses a single character
  def char(c: Char): Parser[Char] = string(c.toString) map (_.charAt(0))

  implicit def operators[A](p: Parser[A]) = ParserOps[A](p) // implicit conversion to enable infix ParserOps[A]
  case class ParserOps[A](p: Parser[A]) {
    def |[B>:A](p2: Parser[B]): Parser[B] = self.or(p,p2)
    def or[B>:A](p2: => Parser[B]): Parser[B] = self.or(p,p2)
    def many: Parser[List[A]] = self.many(p)
    def map[B](f: A => B): Parser[B] = self.map(p)(f)
    def **[B](p2: Parser[B]): Parser[(A,B)] = self.product(p, p2)
    def product[B](p2: Parser[B]): Parser[(A,B)] = self.product(p, p2)
  }
{'fraction_non_alphanumeric': 0.21080817916260955, 'fraction_numerical': 0.012901655306718598, 'mean_word_length': 4.322538860103627, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '253026', 'n_tokens_mistral': 1756, 'n_tokens_neox': 1706, 'n_words': 396}