From 9370a2adf0404d4588a27d61dd88e4ca7be9ffd4 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Wed, 7 Feb 2018 16:10:48 +0100 Subject: [PATCH 1/2] Speedup Parsers.validID It turned up in profiling sessions. Previously, it used parser combinators which are somewhat slow especially when the JVM is still cold. The grammar for ID is simple enough to afford this handwritten parser. --- .../sbt/internal/util/complete/Parsers.scala | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index b04b61127..c148095ec 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -12,15 +12,17 @@ import Parser._ import java.io.File import java.net.URI import java.lang.Character.{ - getType, - MATH_SYMBOL, - OTHER_SYMBOL, + CURRENCY_SYMBOL, DASH_PUNCTUATION, - OTHER_PUNCTUATION, + MATH_SYMBOL, MODIFIER_SYMBOL, - CURRENCY_SYMBOL + OTHER_PUNCTUATION, + OTHER_SYMBOL, + getType } +import scala.annotation.tailrec + /** Provides standard implementations of commonly useful [[Parser]]s. */ trait Parsers { @@ -313,6 +315,16 @@ object DefaultParsers extends Parsers with ParserMain { apply(p)(s).resultEmpty.isValid /** Returns `true` if `s` parses successfully according to [[ID]].*/ - def validID(s: String): Boolean = matches(ID, s) + def validID(s: String): Boolean = { + // Handwritten version of `matches(ID, s)` because validID turned up in profiling. + def isIdChar(c: Char): Boolean = Character.isLetterOrDigit(c) || (c == '_') + @tailrec def isRestIdChar(cur: Int, s: String, length: Int): Boolean = + if (cur < length) + isIdChar(s.charAt(cur)) && isRestIdChar(cur + 1, s, length) + else + true + + !s.isEmpty && Character.isLetter(s.charAt(0)) && isRestIdChar(1, s, s.length) + } } From d66d0e34a96282f0d3ff68a3e05775c1720e335e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 19 Feb 2018 13:59:51 +0000 Subject: [PATCH 2/2] Add prop-based test for the validID re-impl --- .../src/test/scala/DefaultParsersSpec.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 internal/util-complete/src/test/scala/DefaultParsersSpec.scala diff --git a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala new file mode 100644 index 000000000..ce3a6b2e9 --- /dev/null +++ b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala @@ -0,0 +1,17 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.internal.util +package complete + +import org.scalacheck._, Prop._ + +object DefaultParsersSpec extends Properties("DefaultParsers") { + import DefaultParsers._ + + property("validID == matches(ID, s)") = forAll((s: String) => validID(s) == matches(ID, s)) +}