From 8f3b29538f5f0aa0740569a48ef0e7ac7ee7c230 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Sat, 2 Feb 2019 18:00:29 +0900 Subject: [PATCH 1/6] parse by Token --- core/src/main/scala/parseback/Line.scala | 10 ++++--- .../src/main/scala/parseback/LineStream.scala | 16 ++++++---- core/src/main/scala/parseback/MemoTable.scala | 18 ++++++------ core/src/main/scala/parseback/Token.scala | 25 ++++++++++++++++ core/src/main/scala/parseback/package.scala | 7 ++--- core/src/main/scala/parseback/parsers.scala | 29 +++++++------------ .../scala/parseback/render/Renderer.scala | 4 +-- 7 files changed, 65 insertions(+), 44 deletions(-) create mode 100644 core/src/main/scala/parseback/Token.scala diff --git a/core/src/main/scala/parseback/Line.scala b/core/src/main/scala/parseback/Line.scala index 023474c..8c370e5 100644 --- a/core/src/main/scala/parseback/Line.scala +++ b/core/src/main/scala/parseback/Line.scala @@ -21,11 +21,11 @@ package parseback * @param lineNo The line offset of this line within the larger input stream (0 indexed) * @param colNo The column offset into `base` (0 indexed) */ -final case class Line(base: String, lineNo: Int = 0, colNo: Int = 0) { +final case class Line(base: Array[Token], lineNo: Int = 0, colNo: Int = 0) { - def head: Char = base charAt colNo + def head: Token = base(colNo) - def project: String = base substring colNo + def project: String = base.mkString(" ") def isEmpty: Boolean = base.length == colNo @@ -37,9 +37,11 @@ final case class Line(base: String, lineNo: Int = 0, colNo: Int = 0) { def renderError: String = base + s"${0 until colNo map { _ => ' ' } mkString}^" + + override def toString: String = s"List(${this.head}, ${lineNo}, ${colNo})" } -object Line extends ((String, Int, Int) => Line) { +object Line extends ((Array[Token], Int, Int) => Line) { def addTo(lines: Vector[Line], line: Line): Vector[Line] = { if (lines.isEmpty) { diff --git a/core/src/main/scala/parseback/LineStream.scala b/core/src/main/scala/parseback/LineStream.scala index 2ddb49f..d3ea967 100644 --- a/core/src/main/scala/parseback/LineStream.scala +++ b/core/src/main/scala/parseback/LineStream.scala @@ -38,18 +38,24 @@ sealed trait LineStream[F[+_]] extends Product with Serializable { object LineStream { - def apply[F[+_]: Applicative](str: String): LineStream[F] = { + def apply[F[+_]: Applicative](str: String, tokenizer: String => Array[Token]): LineStream[F] = { if (str.isEmpty) { Empty() } else { - val splits = str split """\r|\r?\n""" - val (front, last) = splits splitAt (splits.length - 1) + val splits: Array[String] = str split """\r|\r?\n""" + apply(splits.map(tokenizer)) + } + } - apply((front map { _ + "\n" }) ++ last) + def apply[F[+_]: Applicative](line: Array[Token]): LineStream[F] = { + if (line.isEmpty) { + Empty() + } else { + apply(Seq(line)) } } - def apply[F[+_]: Applicative](lines: Seq[String]): LineStream[F] = { + def apply[F[+_]: Applicative](lines: Seq[Array[Token]]): LineStream[F] = { val actuals = lines.zipWithIndex map { case (str, lineNo) => Line(str, lineNo, 0) } diff --git a/core/src/main/scala/parseback/MemoTable.scala b/core/src/main/scala/parseback/MemoTable.scala index 48e54bd..2368d7e 100644 --- a/core/src/main/scala/parseback/MemoTable.scala +++ b/core/src/main/scala/parseback/MemoTable.scala @@ -22,8 +22,8 @@ import java.util.HashMap // TODO it may be possible to retain SOME results between derivations (just not those which involve Apply) private[parseback] sealed abstract class MemoTable { - def derived[A](from: Parser[A], c: Char, to: Parser[A]): this.type - def derive[A](from: Parser[A], c: Char): Option[Parser[A]] + def derived[A](from: Parser[A], c: Token, to: Parser[A]): this.type + def derive[A](from: Parser[A], c: Token): Option[Parser[A]] def finished[A](target: Parser[A], results: Results.Cacheable[A]): this.type def finish[A](target: Parser[A]): Option[Results.Cacheable[A]] @@ -48,22 +48,22 @@ private[parseback] final class InitialMemoTable extends MemoTable { import MemoTable._ // still using the single-derivation optimization here - private val derivations: HashMap[(MemoTable, ParserId[_]), (Char, Parser[_])] = new HashMap(16) // TODO tune capacities + private val derivations: HashMap[(MemoTable, ParserId[_]), (Token, Parser[_])] = new HashMap(16) // TODO tune capacities private val finishes: HashMap[(MemoTable, ParserId[_]), Results.Cacheable[_]] = new HashMap(16) - def derived[A](from: Parser[A], c: Char, to: Parser[A]): this.type = + def derived[A](from: Parser[A], c: Token, to: Parser[A]): this.type = derived(this, from, c, to) - private[parseback] def derived[A](table: MemoTable, from: Parser[A], c: Char, to: Parser[A]): this.type = { + private[parseback] def derived[A](table: MemoTable, from: Parser[A], c: Token, to: Parser[A]): this.type = { derivations.put((table, new ParserId(from)), (c, to)) this } - def derive[A](from: Parser[A], c: Char): Option[Parser[A]] = + def derive[A](from: Parser[A], c: Token): Option[Parser[A]] = derive(this, from, c) - private[parseback] def derive[A](table: MemoTable, from: Parser[A], c: Char): Option[Parser[A]] = { + private[parseback] def derive[A](table: MemoTable, from: Parser[A], c: Token): Option[Parser[A]] = { val back = derivations.get((table, new ParserId(from))) if (back != null && back._1 == c) @@ -92,7 +92,7 @@ private[parseback] final class InitialMemoTable extends MemoTable { private[parseback] final class FieldMemoTable(delegate: InitialMemoTable) extends MemoTable { - def derived[A](from: Parser[A], c: Char, to: Parser[A]): this.type = { + def derived[A](from: Parser[A], c: Token, to: Parser[A]): this.type = { if (from.isRoot) { delegate.derived(this, from, c, to) } else { @@ -104,7 +104,7 @@ private[parseback] final class FieldMemoTable(delegate: InitialMemoTable) extend this } - def derive[A](from: Parser[A], c: Char): Option[Parser[A]] = { + def derive[A](from: Parser[A], c: Token): Option[Parser[A]] = { if (from.isRoot) { delegate.derive(this, from, c) } else { diff --git a/core/src/main/scala/parseback/Token.scala b/core/src/main/scala/parseback/Token.scala new file mode 100644 index 0000000..6743fdb --- /dev/null +++ b/core/src/main/scala/parseback/Token.scala @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Daniel Spiewak + * + * 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 parseback + +trait Token { + def toString: String +} + +object Token { + def apply(str: String): Token = new Token { override def toString: String = str } +} \ No newline at end of file diff --git a/core/src/main/scala/parseback/package.scala b/core/src/main/scala/parseback/package.scala index 6134907..3774a0b 100644 --- a/core/src/main/scala/parseback/package.scala +++ b/core/src/main/scala/parseback/package.scala @@ -20,7 +20,7 @@ package object parseback { // TODO macroize private[parseback] def trace(str: => String): Unit = { - // println(str) + //println(str) } // external syntax @@ -29,10 +29,7 @@ package object parseback { val ~ = Tuple2 implicit def literal(str: String): Parser[String] = { - if (str.isEmpty) - Parser.Epsilon("") - else - Parser.Literal(str, 0) + Parser.Literal(str) } implicit def literalLazy(str: String): LazyParserSyntax[String] = diff --git a/core/src/main/scala/parseback/parsers.scala b/core/src/main/scala/parseback/parsers.scala index 21fed57..7e24432 100644 --- a/core/src/main/scala/parseback/parsers.scala +++ b/core/src/main/scala/parseback/parsers.scala @@ -39,7 +39,7 @@ sealed trait Parser[+A] { private[parseback] var isRoot: Boolean = false private[parseback] var derivedTable: FieldMemoTable = _ - private[parseback] var derivedC: Char = _ + private[parseback] var derivedC: Token = _ private[parseback] var derivedR: Parser[A @uncheckedVariance] = _ private[parseback] var finishedTable: FieldMemoTable = _ @@ -81,7 +81,7 @@ sealed trait Parser[+A] { def *(): Parser[List[A]] = { lazy val back: Parser[List[A]] = ( this ~ back ^^ { (_, h, t) => h :: t } - | "" ^^^ Nil + | Parser.Epsilon(()) ^^^ Nil ) back @@ -204,7 +204,7 @@ sealed trait Parser[+A] { // the following four cases should never be hit, but they // are correctly defined here for documentation purposes - case p @ Literal(_, _) => + case p @ Literal(_) => p.nullableMemo = False False @@ -479,28 +479,22 @@ object Parser { protected def _finish(seen: Set[ParserId[_]], table: MemoTable) = // if the Result is unique, no need to filter out. - target.finish(seen, table) pmap { c => if(c.length == 1 && leaveOne) c else c filter p } + target.finish(seen, table) pmap { c => if(leaveOne && c.length == 1) c else c filter p } } - final case class Literal(literal: String, offset: Int = 0) extends Parser[String] { - require(literal.length > 0) - require(offset < literal.length) - + final case class Literal(literal: String) extends Parser[String] { nullableMemo = Nullable.False protected def _derive(line: Line, table: MemoTable): Parser[String] = { - if (literal.charAt(offset) == line.head) { - if (offset == literal.length - 1) + if (literal == line.head.toString) { Epsilon(literal) - else - Literal(literal, offset + 1) } else { - Failure(ParseError.UnexpectedCharacter(line, Set(literal substring offset)) :: Nil) + Failure(ParseError.UnexpectedCharacter(line, Set(literal)) :: Nil) } } protected def _finish(seen: Set[ParserId[_]], table: MemoTable) = - Results.Failure(ParseError.UnexpectedEOF(Set(literal substring offset)) :: Nil) + Results.Failure(ParseError.UnexpectedEOF(Set(literal.toString)) :: Nil) } // note that regular expressions cannot cross line boundaries @@ -510,13 +504,10 @@ object Parser { nullableMemo = Nullable.False protected def _derive(line: Line, table: MemoTable): Parser[String] = { - val m = r findPrefixOf line.project + val m = r findPrefixOf line.head.toString val success = m map { lit => - if (lit.length == 1) - Epsilon(lit) - else - Literal(lit, 1) + Epsilon(lit) } success getOrElse Failure(ParseError.UnexpectedCharacter(line, Set(r.toString)) :: Nil) diff --git a/core/src/main/scala/parseback/render/Renderer.scala b/core/src/main/scala/parseback/render/Renderer.scala index eac42c9..417fc79 100644 --- a/core/src/main/scala/parseback/render/Renderer.scala +++ b/core/src/main/scala/parseback/render/Renderer.scala @@ -130,8 +130,8 @@ object Renderer { case Filter(target, _, _) => State pure (Left(target) :: Nil) - case Literal(literal, offset) => - State pure ((s"'${literal substring offset}'" :: Nil) map { Right(_) }) + case Literal(literal) => + State pure ((s"'${literal.toString}'" :: Nil) map { Right(_) }) case Regex(r) => State pure ((s"/${r.pattern}/" :: Nil) map { Right(_) }) From c1b360140c3b3a46ef4799197dfd449f77a30350 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Sat, 2 Feb 2019 18:08:54 +0900 Subject: [PATCH 2/6] remove Whitespace --- .../src/main/scala/parseback/Whitespace.scala | 26 ------- core/src/main/scala/parseback/parsers.scala | 78 +++++-------------- .../scala/parseback/render/Renderer.scala | 2 +- 3 files changed, 22 insertions(+), 84 deletions(-) delete mode 100644 core/src/main/scala/parseback/Whitespace.scala diff --git a/core/src/main/scala/parseback/Whitespace.scala b/core/src/main/scala/parseback/Whitespace.scala deleted file mode 100644 index ed02dbb..0000000 --- a/core/src/main/scala/parseback/Whitespace.scala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2018 Daniel Spiewak - * - * 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 parseback - -final case class Whitespace(layout: Option[Parser[_]]) extends AnyVal - -object Whitespace { - implicit val Default = Whitespace(None) - - def apply(layout: Parser[_]): Whitespace = - Whitespace(Some(layout)) -} diff --git a/core/src/main/scala/parseback/parsers.scala b/core/src/main/scala/parseback/parsers.scala index 7e24432..61a4129 100644 --- a/core/src/main/scala/parseback/parsers.scala +++ b/core/src/main/scala/parseback/parsers.scala @@ -58,17 +58,13 @@ sealed trait Parser[+A] { // filter out but at least leave one element. def filterLeaveOne(p: A => Boolean): Parser[A] = Parser.Filter(this, true, p) - // TODO come up with a better name - final def ~!~[B](that: Parser[B]): Parser[A ~ B] = - Parser.sequence(this, None, that) + final def ~[B](that: Parser[B]): Parser[A ~ B] = + Parser.sequence(this, that) - final def ~[B](that: Parser[B])(implicit W: Whitespace): Parser[A ~ B] = - Parser.sequence(this, W.layout, that) - - final def ~>[B](that: Parser[B])(implicit W: Whitespace): Parser[B] = + final def ~>[B](that: Parser[B]): Parser[B] = (this ~ that) map { case _ ~ b => b } - final def <~[B](that: Parser[B])(implicit W: Whitespace): Parser[A] = + final def <~[B](that: Parser[B]): Parser[A] = (this ~ that) map { case a ~ _ => a } def ^^^[B](b: B): Parser[B] = map { _ => b } @@ -106,7 +102,7 @@ sealed trait Parser[+A] { * Please note that F[_] should be lazy and stack-safe. If F[_] is not stack-safe, * this function will SOE on inputs with a very large number of lines. */ - final def apply[F[+_]: Monad](ls: LineStream[F])(implicit W: Whitespace): F[Either[List[ParseError], Catenable[A]]] = { + final def apply[F[+_]: Monad](ls: LineStream[F]): F[Either[List[ParseError], Catenable[A]]] = { import LineStream._ def markRoots(self: Parser[_], tracked: Set[ParserId[_]]): Set[ParserId[_]] = { @@ -122,11 +118,10 @@ sealed trait Parser[+A] { self.isRoot = true self match { - case Sequence(left, layout, right) => + case Sequence(left, right) => val tracked3 = markRoots(left, tracked2) - val tracked4 = layout map { markRoots(_, tracked3) } getOrElse tracked3 - markRoots(right, tracked4) + markRoots(right, tracked3) case self @ Union(_, _) => markRoots(self.right, markRoots(self.left, tracked2)) @@ -160,15 +155,9 @@ sealed trait Parser[+A] { } } - val wrapped = W.layout map { ws => - ws ~!~ this ~!~ ws map { - case ((_, v), _) => v // we don't care about whitespace results - } - } getOrElse this - - markRoots(wrapped, Set.empty) + markRoots(this, Set.empty) - inner(wrapped, new InitialMemoTable)(ls) + inner(this, new InitialMemoTable)(ls) } protected[parseback] final def isNullable: Boolean = { @@ -182,10 +171,8 @@ sealed trait Parser[+A] { p.nullableMemo } else { p match { - case p @ Sequence(left, layout, right) => - val wsNullable = layout map { inner(_, tracked) } getOrElse True - - p.nullableMemo = inner(left, tracked) && wsNullable && inner(right, tracked) + case p @ Sequence(left, right) => + p.nullableMemo = inner(left, tracked) && inner(right, tracked) p.nullableMemo case p @ Union(_, _) => @@ -338,17 +325,13 @@ object Parser { /** * Two sequentialized parsers with optionally interleaving whitespace. */ - final case class Sequence[+A, +B](left: Parser[A], layout: Option[Parser[_]], right: Parser[B]) extends Parser[A ~ B] { - nullableMemo = { - val wsNullable = layout map { _.nullableMemo } getOrElse Nullable.True - - left.nullableMemo && wsNullable && right.nullableMemo - } + final case class Sequence[+A, +B](left: Parser[A], right: Parser[B]) extends Parser[A ~ B] { + nullableMemo = left.nullableMemo && right.nullableMemo protected def _derive(line: Line, table: MemoTable): Parser[A ~ B] = { trace(s">> deriving $this") - val nonNulled = sequence(left.derive(line, table), layout, right) + val nonNulled = sequence(left.derive(line, table), right) if (left.isNullable) { trace(s" >> left is nullable") @@ -361,16 +344,7 @@ object Parser { case Left(_) => nonNulled case Right(results) => - // iff we have interleaving whitespace, rewrite self to - // have whitespace be the new left, interleaved with no - // new whitespace - val rhs = layout map { ws => - sequence(ws, None, right) map { - case (_, r) => r - } - } getOrElse right - - val nulled = Parser.apply(rhs.derive(line, table), { (_, b: B) => + val nulled = Parser.apply(right.derive(line, table), { (_, b: B) => results map { (_, b) } }) @@ -383,25 +357,17 @@ object Parser { } protected def _finish(seen: Set[ParserId[_]], table: MemoTable) = { - val wsFinish = - layout map { _.finish(seen, table) } getOrElse Results.Success(Catenable(())) - - val leftFinish = (left.finish(seen, table) && wsFinish) map { - case (a, _) => a - } - - leftFinish && right.finish(seen, table) + left.finish(seen, table) && right.finish(seen, table) } } private[parseback] def sequence[A, B]( left: Parser[A], - layout: Option[Parser[_]], right: Parser[B]): Parser[A ~ B] = { left match { - case Sequence(innerLeft, innerLayout, innerRight) => - sequence(innerLeft, innerLayout, sequence(innerRight, layout, right)) map { + case Sequence(innerLeft, innerRight) => + sequence(innerLeft, sequence(innerRight, right)) map { case (a, (b, c)) => ((a, b), c) } @@ -412,16 +378,14 @@ object Parser { left.f(lines, e) map { (_, b) } } - apply(sequence(left.target, layout, right), f, left.lines) + apply(sequence(left.target, right), f, left.lines) case Epsilon(value) => - layout map { ws => - sequence(ws, None, right) map { case (_, b) => (value, b) } - } getOrElse (right map { (value, _) }) + right map { (value, _) } case f @ Failure(_) => f - case _ => Sequence(left, layout, right) + case _ => Sequence(left, right) } } diff --git a/core/src/main/scala/parseback/render/Renderer.scala b/core/src/main/scala/parseback/render/Renderer.scala index 417fc79..dc4d1e7 100644 --- a/core/src/main/scala/parseback/render/Renderer.scala +++ b/core/src/main/scala/parseback/render/Renderer.scala @@ -103,7 +103,7 @@ object Renderer { } def render(self: Parser[_]): State[RenderState, RenderResult.TokenSequence] = self match { - case Sequence(left, _, right) => + case Sequence(left, right) => State pure (Left(left) :: Left(right) :: Nil) case Union(_, _) => From ca66f6dc5e87ca4b30cdd97466e783076da0dc50 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Sun, 3 Feb 2019 14:24:41 +0900 Subject: [PATCH 3/6] add LexerHelper --- .../main/scala/parseback/LexerHelper.scala | 90 +++++++++++++++++++ .../src/main/scala/parseback/LineStream.scala | 4 +- 2 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 core/src/main/scala/parseback/LexerHelper.scala diff --git a/core/src/main/scala/parseback/LexerHelper.scala b/core/src/main/scala/parseback/LexerHelper.scala new file mode 100644 index 0000000..9afd6fe --- /dev/null +++ b/core/src/main/scala/parseback/LexerHelper.scala @@ -0,0 +1,90 @@ +/* + * Copyright 2018 Daniel Spiewak + * + * 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 parseback + +import scala.util.matching.Regex + +object LexerHelper { + + def lexer(whitespace: Regex, keywords: Set[String], symbols: Set[String], others: Set[Regex]): String => Array[Token] = { str: String => + type R = (Option[Token], Int) + + def lexingW(str: String, index: Int): Option[R] = { + whitespace.findPrefixOf(str) match { + case Some(space) => + Option((None, index + space.length)) + case None => + Option.empty + } + } + + def lexingR(rs: Set[Regex], str: String, index: Int): Option[R] = { + val k = rs.flatMap(r => r.findPrefixOf(str)) + if(k.nonEmpty) { + val m = k.maxBy(k => trim(k).length) + Option((Some(Token(trim(m))), index + m.length)) + } else { + Option.empty + } + } + + def lexingS(rs: Set[String], str: String, index: Int): Option[R] = { + rs.filter(s => str.startsWith(s)) match { + case s if s.nonEmpty => + val m = s.maxBy(_.length) + Option((Some(Token(m)), index + m.length)) + case _ => + Option.empty + } + } + + def trim(s:String): String = { + whitespace.replaceAllIn(s, "") + } + + def loop(index: Int): (Option[Token], Int) = { + val sub = str.substring(index) + lexingW(sub, index) getOrElse { + lexingR(keywords.map(r => s"${r}${whitespace.pattern}".r), sub, index) getOrElse { + lexingS(symbols, sub, index) getOrElse { + lexingR(others, sub, index) getOrElse { + throw new Exception(s"lexing fail at $sub") + } + } + } + } + } + def unfold[A, B](until: A => Boolean, h: A => (Option[B], A), a: A): List[B] = + if (until(a)) + Nil + else { + h(a) match { + case (Some(v), n) => + v :: unfold(until, h, n) + case (None, n) => + unfold(until, h, n) + } + } + + if(str.isEmpty) { + Array.empty + } else { + unfold[Int, Token](index => str.length <= index, loop, 0).toArray + } + } + +} diff --git a/core/src/main/scala/parseback/LineStream.scala b/core/src/main/scala/parseback/LineStream.scala index d3ea967..4e416ae 100644 --- a/core/src/main/scala/parseback/LineStream.scala +++ b/core/src/main/scala/parseback/LineStream.scala @@ -38,12 +38,12 @@ sealed trait LineStream[F[+_]] extends Product with Serializable { object LineStream { - def apply[F[+_]: Applicative](str: String, tokenizer: String => Array[Token]): LineStream[F] = { + def apply[F[+_]: Applicative](str: String, lexer: String => Array[Token]): LineStream[F] = { if (str.isEmpty) { Empty() } else { val splits: Array[String] = str split """\r|\r?\n""" - apply(splits.map(tokenizer)) + apply(splits.map(lexer)) } } From 44eaa7baaeb9ae8fda8b75c17cd1bd84a4b08618 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Sun, 3 Feb 2019 17:03:00 +0900 Subject: [PATCH 4/6] fix test. --- .../benchmarks/ArithmeticBenchmark.scala | 3 ++ .../main/scala/parseback/LexerHelper.scala | 10 +++--- core/src/main/scala/parseback/Line.scala | 18 +++++++++-- core/src/main/scala/parseback/Token.scala | 6 ++-- core/src/main/scala/parseback/parsers.scala | 6 ++-- .../test/scala/parseback/ArithmeticSpec.scala | 8 +++-- .../scala/parseback/CompoundParserSpec.scala | 32 +++++++++---------- core/src/test/scala/parseback/EBNFSpec.scala | 3 +- core/src/test/scala/parseback/ErrorSpec.scala | 6 ++-- core/src/test/scala/parseback/LineSpec.scala | 13 +++++--- .../test/scala/parseback/ParsebackSpec.scala | 9 +++--- core/src/test/scala/parseback/RegexSpec.scala | 14 ++++---- .../scala/parseback/SimpleParserSpec.scala | 3 ++ .../scala/parseback/ast/FilterSpecs.scala | 6 ++-- 14 files changed, 80 insertions(+), 57 deletions(-) diff --git a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala index 8c07393..3ee8578 100644 --- a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala +++ b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala @@ -17,6 +17,7 @@ package parseback.benchmarks import org.openjdk.jmh.annotations._ +import parseback.LexerHelper import scala.util.parsing.{combinator => spc} @@ -129,6 +130,8 @@ class ArithmeticBenchmarks { import _root_.parseback.LineStream import cats.Eval + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option.empty, Set.empty, Set("+", "-", "*", "/", "(", ")"), Set(numR)) val stream = LineStream[Eval](sample(size)) parseback(stream).value diff --git a/core/src/main/scala/parseback/LexerHelper.scala b/core/src/main/scala/parseback/LexerHelper.scala index 9afd6fe..b184e43 100644 --- a/core/src/main/scala/parseback/LexerHelper.scala +++ b/core/src/main/scala/parseback/LexerHelper.scala @@ -20,16 +20,16 @@ import scala.util.matching.Regex object LexerHelper { - def lexer(whitespace: Regex, keywords: Set[String], symbols: Set[String], others: Set[Regex]): String => Array[Token] = { str: String => + def lexer(whitespace: Option[Regex], keywords: Set[String], symbols: Set[String], others: Set[Regex]): String => Array[Token] = { str: String => type R = (Option[Token], Int) def lexingW(str: String, index: Int): Option[R] = { - whitespace.findPrefixOf(str) match { + whitespace.flatMap(_.findPrefixOf(str) match { case Some(space) => Option((None, index + space.length)) case None => Option.empty - } + }) } def lexingR(rs: Set[Regex], str: String, index: Int): Option[R] = { @@ -53,13 +53,13 @@ object LexerHelper { } def trim(s:String): String = { - whitespace.replaceAllIn(s, "") + whitespace.map(_.replaceAllIn(s, "")).getOrElse(s) } def loop(index: Int): (Option[Token], Int) = { val sub = str.substring(index) lexingW(sub, index) getOrElse { - lexingR(keywords.map(r => s"${r}${whitespace.pattern}".r), sub, index) getOrElse { + lexingR(keywords.map(r => s"${r}${whitespace.map(_.pattern).getOrElse("")}".r), sub, index) getOrElse { lexingS(symbols, sub, index) getOrElse { lexingR(others, sub, index) getOrElse { throw new Exception(s"lexing fail at $sub") diff --git a/core/src/main/scala/parseback/Line.scala b/core/src/main/scala/parseback/Line.scala index 8c370e5..8636267 100644 --- a/core/src/main/scala/parseback/Line.scala +++ b/core/src/main/scala/parseback/Line.scala @@ -25,7 +25,7 @@ final case class Line(base: Array[Token], lineNo: Int = 0, colNo: Int = 0) { def head: Token = base(colNo) - def project: String = base.mkString(" ") + def project: String = base.map(_.value).mkString(" ") def isEmpty: Boolean = base.length == colNo @@ -36,9 +36,21 @@ final case class Line(base: Array[Token], lineNo: Int = 0, colNo: Int = 0) { this.lineNo < that.lineNo || (this.lineNo == that.lineNo && this.colNo < that.colNo) def renderError: String = - base + s"${0 until colNo map { _ => ' ' } mkString}^" + project + s"${0 until colNo map { _ => ' ' } mkString}^" - override def toString: String = s"List(${this.head}, ${lineNo}, ${colNo})" + // due to Array. + override def equals(thatGeneric: scala.Any): Boolean = { + if(!thatGeneric.isInstanceOf[Line]) + return false + + val that = thatGeneric.asInstanceOf[Line] + val thisBase = if(this.base == null) null else this.base.deep + val thatBase = if(that.base == null) null else that.base.deep + + (thisBase, lineNo, colNo) == ((thatBase, that.lineNo, that.colNo)) + } + + override def toString: String = s"Line(${project}, ${lineNo}, ${colNo})" } object Line extends ((Array[Token], Int, Int) => Line) { diff --git a/core/src/main/scala/parseback/Token.scala b/core/src/main/scala/parseback/Token.scala index 6743fdb..80ba7e7 100644 --- a/core/src/main/scala/parseback/Token.scala +++ b/core/src/main/scala/parseback/Token.scala @@ -16,10 +16,8 @@ package parseback -trait Token { - def toString: String -} +case class Token(value: String) object Token { - def apply(str: String): Token = new Token { override def toString: String = str } + def apply(strs: String*): Array[Token] = strs.toArray.map(s=>Token(s)) } \ No newline at end of file diff --git a/core/src/main/scala/parseback/parsers.scala b/core/src/main/scala/parseback/parsers.scala index 61a4129..d2b5002 100644 --- a/core/src/main/scala/parseback/parsers.scala +++ b/core/src/main/scala/parseback/parsers.scala @@ -450,7 +450,7 @@ object Parser { nullableMemo = Nullable.False protected def _derive(line: Line, table: MemoTable): Parser[String] = { - if (literal == line.head.toString) { + if (literal == line.head.value) { Epsilon(literal) } else { Failure(ParseError.UnexpectedCharacter(line, Set(literal)) :: Nil) @@ -458,7 +458,7 @@ object Parser { } protected def _finish(seen: Set[ParserId[_]], table: MemoTable) = - Results.Failure(ParseError.UnexpectedEOF(Set(literal.toString)) :: Nil) + Results.Failure(ParseError.UnexpectedEOF(Set(literal)) :: Nil) } // note that regular expressions cannot cross line boundaries @@ -468,7 +468,7 @@ object Parser { nullableMemo = Nullable.False protected def _derive(line: Line, table: MemoTable): Parser[String] = { - val m = r findPrefixOf line.head.toString + val m = r findPrefixOf line.head.value val success = m map { lit => Epsilon(lit) diff --git a/core/src/test/scala/parseback/ArithmeticSpec.scala b/core/src/test/scala/parseback/ArithmeticSpec.scala index f43f906..9420554 100644 --- a/core/src/test/scala/parseback/ArithmeticSpec.scala +++ b/core/src/test/scala/parseback/ArithmeticSpec.scala @@ -20,7 +20,9 @@ object ArithmeticSpec extends ParsebackSpec { import ParseError._ "expression evaluator" should { - implicit val W = Whitespace(() | """\s+""".r) + val whitespace = """\s+""".r + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option(whitespace), Set.empty, Set("+", "-", "*", "/", "(", ")"), Set(numR)) lazy val expr: Parser[Int] = ( expr ~ "+" ~ term ^^ { (_, e, _, t) => e + t } @@ -37,7 +39,7 @@ object ArithmeticSpec extends ParsebackSpec { lazy val factor: Parser[Int] = ( "(" ~> expr <~ ")" | "-" ~ expr ^^ { (_, _, e) => -e } - | """\d+""".r ^^ { (_, str) => str.toInt } + | numR ^^ { (_, str) => str.toInt } ) "read and eval `1 + 2`" in { @@ -50,7 +52,7 @@ object ArithmeticSpec extends ParsebackSpec { } "reject partial expressions" in { - expr must failToParse("3 + *")(UnexpectedCharacter(Line("3 + *", 0, 4), Set("""\d+""", "-", "("))) + expr must failToParse("3 + *")(UnexpectedCharacter(Line(Token("3", "+", "*"), 0, 2), Set("""\d+""", "-", "("))) } } } diff --git a/core/src/test/scala/parseback/CompoundParserSpec.scala b/core/src/test/scala/parseback/CompoundParserSpec.scala index 086cb8b..319328a 100644 --- a/core/src/test/scala/parseback/CompoundParserSpec.scala +++ b/core/src/test/scala/parseback/CompoundParserSpec.scala @@ -20,6 +20,8 @@ object CompoundParserSpec extends ParsebackSpec { import ParseError._ "compound non-terminal parsers" should { + implicit val lexer = LexerHelper.lexer(None, Set.empty, Set.empty, Set("[a-z]".r)) + "parse an unambiguous right-recursive grammar" in { lazy val p: Parser[String] = ( "a" ~ p ^^ { (_, a, p) => a + p } @@ -158,13 +160,8 @@ object CompoundParserSpec extends ParsebackSpec { } "parse an unambiguous arithmetic grammar" in { - implicit val W = Whitespace({ - implicitly[Whitespace] mustEqual Whitespace.Default - - lazy val p: Parser[Any] = () | p ~ """\s+""".r - - p - }) + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option("""\s+""".r), Set.empty, Set("+", "-", "*", "/", "(", ")"), Set(numR)) lazy val expr: Parser[Int] = ( expr ~ "+" ~ term ^^ { (_, e1, _, e2) => e1 + e2 } @@ -181,7 +178,7 @@ object CompoundParserSpec extends ParsebackSpec { lazy val factor: Parser[Int] = ( "(" ~> expr <~ ")" | "-" ~> factor ^^ { (_, i) => -i } - | """\d+""".r ^^ { (_, str) => str.toInt } + | numR ^^ { (_, str) => str.toInt } ) val input = """ @@ -201,7 +198,7 @@ object CompoundParserSpec extends ParsebackSpec { case class TicVar(id: String) extends Expr case class Dispatch(actuals: Vector[Expr]) extends Expr - implicit val W = Whitespace(() | """\s+""".r) + implicit val lexer = LexerHelper.lexer(Option("""\s+""".r), Set.empty, Set(",", ":=", "(", ")"), Set("[a-z]".r)) lazy val expr: Parser[Expr] = ( "(" ~ formals ~ ")" ~ ":=" ^^ { (_, _, fs, _, _) => Binding(fs) } @@ -220,10 +217,11 @@ object CompoundParserSpec extends ParsebackSpec { } "handle nested left-recursion" in { - implicit val W = Whitespace(() | """\s+""".r) + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option("""\s+""".r), Set.empty, Set(",", "(", ")"), Set(numR)) lazy val exp: Parser[Any] = ( - n + numR | "(" ~ commaExps ~ ")" | exp ~ exp ) ^^^ null @@ -233,8 +231,6 @@ object CompoundParserSpec extends ParsebackSpec { | commaExps ~ "," ~ exp ) - lazy val n = """\d+"""r - exp must recognize("(0,0) 2") } @@ -299,6 +295,7 @@ object CompoundParserSpec extends ParsebackSpec { }*/ "correctly globally disambiguate a local sequence ambiguity" in { + implicit val lexer = LexerHelper.lexer(None, Set.empty, Set(":="), Set("[a-z]".r, "[0-9]".r)) lazy val expr: Parser[Any] = ( id ~ ":=" ~ expr ~ expr | num @@ -311,6 +308,7 @@ object CompoundParserSpec extends ParsebackSpec { } "successfully process a peculiar mutual left-recursion" in { + implicit val lexer = LexerHelper.lexer(None, Set.empty, Set(",", "+"), Set("[a-z]".r)) lazy val expr: Parser[Any] = ( prefix ~ "b" | "a" @@ -326,6 +324,7 @@ object CompoundParserSpec extends ParsebackSpec { } "parse a reduced expression form without missing the primary terms" in { + implicit val lexer = LexerHelper.lexer(None, Set.empty, Set("+", "*"), Set("1".r)) lazy val expr: Parser[Int] = ( expr <~ "+" ^^ { (_, e) => e + 1 } | term @@ -340,7 +339,9 @@ object CompoundParserSpec extends ParsebackSpec { } "globally disambiguate a local sequence ambiguity" in { - implicit val W = Whitespace(() | """\s+""".r) + lazy val id = """[a-zA-Z]""".r + lazy val num = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option("""\s+""".r), Set.empty, Set(":="), Set(id, num)) lazy val expr: Parser[Any] = ( id ~ ":=" ~ expr ~ expr @@ -348,9 +349,6 @@ object CompoundParserSpec extends ParsebackSpec { | id ) - lazy val id = """[a-zA-Z]""".r - lazy val num = """\d+""".r - expr must recognize("a := 1 c := 2 3", ambiguous = false) } } diff --git a/core/src/test/scala/parseback/EBNFSpec.scala b/core/src/test/scala/parseback/EBNFSpec.scala index 1bdcce8..ad3ad60 100644 --- a/core/src/test/scala/parseback/EBNFSpec.scala +++ b/core/src/test/scala/parseback/EBNFSpec.scala @@ -18,6 +18,7 @@ package parseback object EBNFSpec extends ParsebackSpec { import ParseError._ + implicit val lexer = LexerHelper.lexer(None, Set.empty, Set.empty, Set("abc".r)) "p?" should { val p = literal("abc")? @@ -31,7 +32,7 @@ object EBNFSpec extends ParsebackSpec { } "reject multi" in { - p must failToParse("abcabcabc")(UnexpectedTrailingCharacters(Line("abcabcabc", 0, 3))) + p must failToParse("abcabcabc")(UnexpectedTrailingCharacters(Line(Token("abc", "abc", "abc"), 0, 1))) } } diff --git a/core/src/test/scala/parseback/ErrorSpec.scala b/core/src/test/scala/parseback/ErrorSpec.scala index 6c42420..6522b3f 100644 --- a/core/src/test/scala/parseback/ErrorSpec.scala +++ b/core/src/test/scala/parseback/ErrorSpec.scala @@ -22,16 +22,16 @@ object ErrorSpec extends Specification { import ParseError._ "error rendering" should { - val line = Line("Lorem ipsum dolor sit amet\n", lineNo = 0, colNo = 6) + val line = Line(Token("Lorem", "ipsum", "dolor", "sit", "amet", "\n"), lineNo = 0, colNo = 3) "print trailing characters" in { val error = UnexpectedTrailingCharacters(line) - error.render("ErrorSpec.scala") mustEqual "ErrorSpec.scala:1: unexpected trailing characters\nLorem ipsum dolor sit amet\n ^" + error.render("ErrorSpec.scala") mustEqual "ErrorSpec.scala:1: unexpected trailing characters\nLorem ipsum dolor sit amet \n ^" } "print unexpected characters" in { val error = UnexpectedCharacter(line, Set("foo", "bar")) - error.render("ErrorSpec.scala") mustEqual "ErrorSpec.scala:1: unexpected characters; expected 'foo' or 'bar'\nLorem ipsum dolor sit amet\n ^" + error.render("ErrorSpec.scala") mustEqual "ErrorSpec.scala:1: unexpected characters; expected 'foo' or 'bar'\nLorem ipsum dolor sit amet \n ^" } "print unexpected eof" in { diff --git a/core/src/test/scala/parseback/LineSpec.scala b/core/src/test/scala/parseback/LineSpec.scala index 63ceb94..f021691 100644 --- a/core/src/test/scala/parseback/LineSpec.scala +++ b/core/src/test/scala/parseback/LineSpec.scala @@ -20,18 +20,21 @@ import org.specs2.mutable._ object LineSpec extends Specification { + val data1 = Token("Lorem", "ipsum", "dolor", "sit", "amet", "\n") + val data2 = Token("to", "be", "or", "not", "to", "be", ",", "that", "is", "the", "question") + "line rendering" should { "basically work" in { - val line = Line("Lorem ipsum dolor sit amet\n", lineNo = 0, colNo = 6) + val line = Line(data1, lineNo = 0, colNo = 3) - line.renderError mustEqual "Lorem ipsum dolor sit amet\n ^" + line.renderError mustEqual "Lorem ipsum dolor sit amet \n ^" } } "line accumulation" should { import Line.addTo - val line = Line("Lorem ipsom dolor sit amet") + val line = Line(data1) "add the first line" in { addTo(Vector.empty, line) mustEqual Vector(line) @@ -43,13 +46,13 @@ object LineSpec extends Specification { } "add the second line" in { - val line2 = Line("to be or not to be, that is the question", lineNo = 1) + val line2 = Line(data2, lineNo = 1) addTo(Vector(line), line2) mustEqual Vector(line, line2) } "retain the first line and the columnar-latest subsequent lines" in { - val line2 = Line("to be or not to be, that is the question", lineNo = 1) + val line2 = Line(data2, lineNo = 1) val line3 = line2.next.get val line4 = line3.next.get diff --git a/core/src/test/scala/parseback/ParsebackSpec.scala b/core/src/test/scala/parseback/ParsebackSpec.scala index e77561d..7bb668e 100644 --- a/core/src/test/scala/parseback/ParsebackSpec.scala +++ b/core/src/test/scala/parseback/ParsebackSpec.scala @@ -26,7 +26,7 @@ import scala.util.{Left, Right} trait ParsebackSpec extends Spec with SpecificationFeatures { - final def recognize[A](input: String, ambiguous: Boolean = true)(implicit W: Whitespace): Matcher[Parser[A]] = { p: Parser[A] => + final def recognize[A](input: String, ambiguous: Boolean = true)(implicit lexer: String => Array[Token]): Matcher[Parser[A]] = { p: Parser[A] => val maybeResults = p(LineStream[Eval](input)).value maybeResults match { @@ -40,9 +40,10 @@ trait ParsebackSpec extends Spec with SpecificationFeatures { } } - final def recognizeUnambiguously[A](input: String) = recognize[A](input, false) + final def recognizeUnambiguously[A](input: String)(implicit lexer: String => Array[Token]) = + recognize[A](input, false) - final def parseOk[A](input: String)(results: A*)(implicit W: Whitespace): Matcher[Parser[A]] = { p: Parser[A] => + final def parseOk[A](input: String)(results: A*)(implicit lexer: String => Array[Token]): Matcher[Parser[A]] = { p: Parser[A] => val maybeResults = p(LineStream[Eval](input)).value maybeResults match { @@ -63,7 +64,7 @@ trait ParsebackSpec extends Spec with SpecificationFeatures { } } - final def failToParse(input: String)(errors: ParseError*)(implicit W: Whitespace): Matcher[Parser[_]] = { p: Parser[_] => + final def failToParse(input: String)(errors: ParseError*)(implicit lexer: String => Array[Token]): Matcher[Parser[_]] = { p: Parser[_] => val maybeResults = p(LineStream[Eval](input)).value maybeResults match { diff --git a/core/src/test/scala/parseback/RegexSpec.scala b/core/src/test/scala/parseback/RegexSpec.scala index a78712a..76cf9fb 100644 --- a/core/src/test/scala/parseback/RegexSpec.scala +++ b/core/src/test/scala/parseback/RegexSpec.scala @@ -17,29 +17,30 @@ package parseback object RegexSpec extends ParsebackSpec { + val whitespace = """\s+""".r + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option(whitespace), Set.empty, Set("+", "-", "*", "/"), Set(numR)) "regex parsing" should { "consume an integer literal" in { - regex("""\d+""".r) must parseOk("42")("42") + regex(numR) must parseOk("42")("42") } "handle a simple arithmetic grammar" in { lazy val expr: Parser[Int] = ( expr ~ "+" ~ expr ^^ { (_, a, _, b) => a + b } | expr ~ "-" ~ expr ^^ { (_, a, _, b) => a - b } - | """\d+""".r ^^ { (_, str) => str.toInt } + | numR ^^ { (_, str) => str.toInt } ) expr must parseOk("1+2")(3) } "handle a simple arithmetic grammar with whitespace" in { - implicit val W = Whitespace(() | """\s+""".r) - lazy val expr: Parser[Int] = ( expr ~ "+" ~ expr ^^ { (_, a, _, b) => a + b } | expr ~ "-" ~ expr ^^ { (_, a, _, b) => a - b } - | """\d+""".r ^^ { (_, str) => str.toInt } + | numR ^^ { (_, str) => str.toInt } ) expr must parseOk("1 + 2")(3) @@ -55,12 +56,11 @@ object RegexSpec extends ParsebackSpec { * second to Literal(" ")) */ "handle a simple arithmetic grammar with trailing whitespace" in { - implicit val W = Whitespace(() | """\s+""".r) lazy val expr: Parser[Int] = ( expr ~ "+" ~ expr ^^ { (_, a, _, b) => a + b } | expr ~ "-" ~ expr ^^ { (_, a, _, b) => a - b } - | """\d+""".r ^^ { (_, str) => str.toInt } + | numR ^^ { (_, str) => str.toInt } ) expr must parseOk("1 + 2 ")(3) diff --git a/core/src/test/scala/parseback/SimpleParserSpec.scala b/core/src/test/scala/parseback/SimpleParserSpec.scala index e4a2be1..0a18bc9 100644 --- a/core/src/test/scala/parseback/SimpleParserSpec.scala +++ b/core/src/test/scala/parseback/SimpleParserSpec.scala @@ -19,6 +19,9 @@ package parseback object SimpleParserSpec extends ParsebackSpec { import ParseError._ + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option.empty, Set.empty, Set("(", ")"), Set("[a-z]".r)) + "parentheses parser" should { lazy val p: Parser[Int] = ( "(" ~> p <~ ")" ^^ { (_, i) => i + 1 } diff --git a/core/src/test/scala/parseback/ast/FilterSpecs.scala b/core/src/test/scala/parseback/ast/FilterSpecs.scala index 2ae1c00..d6f8469 100644 --- a/core/src/test/scala/parseback/ast/FilterSpecs.scala +++ b/core/src/test/scala/parseback/ast/FilterSpecs.scala @@ -19,7 +19,9 @@ package ast object FilterSpecs extends ParsebackSpec { - implicit val W = Whitespace("" | """\s+""".r) + val whitespace = """\s+""".r + val numR = """\d+""".r + implicit val lexer = LexerHelper.lexer(Option(whitespace), Set.empty, Set("+", "-", "*", "~", "(", ")"), Set(numR)) "ast filtering" should { "disambiguate left-associativity" in { @@ -163,7 +165,7 @@ object FilterSpecs extends ParsebackSpec { // %% - val num = """\d+""".r map { _.toInt } + val num = numR map { _.toInt } // %% From 1d6169e457a59b946d3e44bfbf7f7cf29c615d28 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Thu, 7 Feb 2019 19:56:44 +0900 Subject: [PATCH 5/6] add white space to benchmark. --- .../scala/parseback/benchmarks/ArithmeticBenchmark.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala index 3ee8578..76ed688 100644 --- a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala +++ b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala @@ -116,7 +116,7 @@ class ArithmeticBenchmarks { else "" - neg + i.toString + operators(i % 4) + neg + i.toString + " " + operators(i % 4) } drop 1 mkString } @@ -131,7 +131,8 @@ class ArithmeticBenchmarks { import cats.Eval val numR = """\d+""".r - implicit val lexer = LexerHelper.lexer(Option.empty, Set.empty, Set("+", "-", "*", "/", "(", ")"), Set(numR)) + val whitespace = """\s+""".r + implicit val lexer = LexerHelper.lexer(Option(whitespace), Set.empty, Set("+", "-", "*", "/", "(", ")"), Set(numR)) val stream = LineStream[Eval](sample(size)) parseback(stream).value From 789da33a5dfb49e1edaafe99ef2ee178862c4e88 Mon Sep 17 00:00:00 2001 From: Satoshi Ogasawara Date: Thu, 7 Feb 2019 19:57:09 +0900 Subject: [PATCH 6/6] fix benchmark as parsable. --- .../main/scala/parseback/benchmarks/ArithmeticBenchmark.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala index 76ed688..a1c4c4d 100644 --- a/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala +++ b/benchmarks/src/main/scala/parseback/benchmarks/ArithmeticBenchmark.scala @@ -122,7 +122,7 @@ class ArithmeticBenchmarks { val sizes = List(2, 4, 8, 16, 32, 64, 128) - sizes.map({ i => i -> inner(i) })(collection.breakOut) + sizes.map({ i => i -> inner(i).dropRight(1) })(collection.breakOut) } @Benchmark