Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
winitzki committed Jul 12, 2024
1 parent e8af115 commit db7588e
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ object Memoize {
instrument: Instrument = null,
): Parsed[T] = {
clearAll()
fastparse.parse(input, parser, verboseFailures, startIndex, instrument)
val result = fastparse.parse(input, parser, verboseFailures, startIndex, instrument)
clearAll()
result
}

def parseInputRaw[T](
Expand All @@ -112,7 +114,9 @@ object Memoize {
enableLogging: Boolean = true,
): ParsingRun[T] = {
clearAll()
fastparse.parseInputRaw(input, parser, verboseFailures, startIndex, traceIndex, instrument, enableLogging)
val result = fastparse.parseInputRaw(input, parser, verboseFailures, startIndex, traceIndex, instrument, enableLogging)
clearAll()
result
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ class MemoizeTest extends FunSuite with TestTimings {
def times2[$: P] = P(other2 ~ ("*" ~ other2).rep).map { case (i, is) => i * is.product }
def other2[$: P]: P[Int] = P(number | ("(" ~ expr2 ~ ")")).memoize

// Warm up JVM.
(1 to 10).foreach { _ =>
Memoize.parse("(" * (n - 1) + "1" + ")" * (n - 1), program2(_))
}

val (result2, elapsed2) = elapsedNanos(Memoize.parse("(" * (n - 1) + "1" + ")" * (n - 1), program2(_)))
assert(result2.get.value == 1)
// Verify that the memoized parser works as expected.
Expand All @@ -46,8 +51,10 @@ class MemoizeTest extends FunSuite with TestTimings {
assert(Memoize.parse("123*1-1", program2(_)).get.value == 122)
assert(Memoize.parse("123*(1-1)", program2(_)).get.value == 0)

println(s"before memoization: ${elapsed1 / 1e9}, after memoization: ${elapsed2 / 1e9}, statistics: ${Memoize.statistics}")
// Memoization should speed up at least 100 times in this example.
expect(elapsed1 > elapsed2 * 100)
println(
s"before memoization: ${elapsed1 / 1e9}, after memoization: ${elapsed2 / 1e9}, speedup: ${elapsed1 / elapsed2}x, Memoize.statistics: ${Memoize.statistics}"
)
// Memoization should speed up at least 200 times in this example, with JVM warmup.
expect(elapsed1 > elapsed2 * 200)
}
}
9 changes: 7 additions & 2 deletions scall-cli/src/test/scala/io/chymyst/dhall/unit/PerfTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,40 @@ class PerfTest extends FunSuite with ResourceFiles with TestTimings {
}
val resultYaml = new String(testOut.toByteArray)
val expectedYaml = new String(Files.readAllBytes(Paths.get(file.getAbsolutePath.replace(".dhall", ".yaml"))))
val elapsedS = elapsedNs.toDouble / 1e9
println(s"Yaml created in $elapsedS seconds")
val elapsed = elapsedNs.toDouble / 1e9
println(s"Yaml created in $elapsed seconds")
expect(resultYaml == expectedYaml)
expect(elapsed / 1e9 < 10.0)
}

test("parse schema.dhall 20 times") {
val file = resourceAsFile("yaml-perftest/schema.dhall").get
val results = (1 to 20).map { i =>
val (_, elapsed) = elapsedNanos(Parser.parseDhallStream(new FileInputStream(file)).get.value.value)
println(s"iteration $i : schema.dhall parsed in ${elapsed / 1e9} seconds")
expect(elapsed / 1e9 < 0.5)
}
}

test("parse renderAs.dhall") {
val file = resourceAsFile("yaml-perftest/renderAs.dhall").get
val (_, elapsed) = elapsedNanos(Parser.parseDhallStream(new FileInputStream(file)).get.value.value)
println(s"Prelude/JSON/renderAs.dhall parsed in ${elapsed / 1e9} seconds")
expect(elapsed / 1e9 < 1.0)
}

test("parse largeExpressionA.dhall") {
val file = resourceAsFile("yaml-perftest/largeExpressionA.dhall").get
val (_, elapsed) = elapsedNanos(Parser.parseDhallStream(new FileInputStream(file)).get.value.value)
println(s"largeExpressionA.dhall parsed in ${elapsed / 1e9} seconds")
expect(elapsed / 1e9 < 0.5)
}

test("parse nested parentheses") {
val n = 30 // More than 30 gives stack overflow.
val input = "(" * n + "1" + ")" * n
val (_, elapsed) = elapsedNanos(Parser.parseDhall(input).get.value.value)
println(s"$n nested parentheses parsed in ${elapsed / 1e9} seconds")
expect(elapsed / 1e9 < 0.5)
}
}
8 changes: 8 additions & 0 deletions scall-core/src/main/scala/io/chymyst/dhall/Grammar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,25 +112,29 @@ object Grammar {
// U+10FFFD = "\uDBFF\uDFFD"
// %x10FFFE_10FFFF = non_characters
)
.memoize

def tab[$: P] = P("\t")

def block_comment[$: P] = P(
"{-" ~/ block_comment_continue // Do not use cut here, because then block comment will fail the entire identifier when parsing "x {- -}" without a following @.
)
.memoize

def block_comment_char[$: P] = P(
CharIn("\u0020-\u007F")
| valid_non_ascii
| tab
| end_of_line
)
.memoize

def block_comment_continue[$: P]: P[Unit] = P(
"-}"
| (block_comment ~/ block_comment_continue)
| (block_comment_char ~ block_comment_continue)
)
.memoize

def not_end_of_line[$: P] = P(
CharIn("\u0020-\u007F") | valid_non_ascii | tab
Expand All @@ -151,6 +155,7 @@ object Grammar {
| line_comment
| block_comment
)
.memoize

def whsp[$: P]: P[Unit] = P(
NoCut(whitespace_chunk.rep)
Expand Down Expand Up @@ -989,6 +994,7 @@ object Grammar {
// "x : t"
| annotated_expression./
)
.memoize

def annotated_expression[$: P]: P[Expression] = P(
operator_expression ~ (whsp ~ ":" ~ whsp1 ~/ expression).?
Expand Down Expand Up @@ -1237,6 +1243,7 @@ object Grammar {
def record_literal_normal_entry[$: P]: P[(Seq[FieldName], Expression)] = P(
(whsp ~ "." ~ whsp ~/ any_label_or_some.map(FieldName)).rep ~ whsp ~ "=" ~ whsp ~/ expression
)
.memoize

def union_type[$: P]: P[Expression] = P(
(union_type_entry ~ (whsp ~ "|" ~ whsp ~ union_type_entry).rep ~ (whsp ~ "|").?).?
Expand All @@ -1248,6 +1255,7 @@ object Grammar {
def union_type_entry[$: P] = P(
any_label_or_some.map(ConstructorName) ~ (whsp ~ ":" ~/ whsp1 ~/ expression).?
)
.memoize

def non_empty_list_literal[$: P]: P[Expression] = P(
"[" ~/ whsp ~ ("," ~ whsp).? ~ expression ~ whsp ~ ("," ~ whsp ~ /* No cut here, or else [, ,] cannot be parsed. */ expression ~ whsp).rep ~ ("," ~/ whsp).? ~ "]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class DhallParserAndCbor1Suite extends DhallTest {
result
}
println(s"Success count: ${results.count(_.isSuccess)}\nFailure count: ${results.count(_.isFailure)}")
TestUtils.requireSuccessAtLeast(284, results)
TestUtils.requireSuccessAtLeast(286, results)
}

test("validate CBOR writing for standard examples") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class DhallParserAndCbor2Suite extends DhallTest {
}
result
}
TestUtils.requireSuccessAtLeast(281, results)
TestUtils.requireSuccessAtLeast(286, results)
}

test("parse standard examples for failed parsing") {
Expand Down Expand Up @@ -66,7 +66,7 @@ class DhallParserAndCbor2Suite extends DhallTest {
if (result.exists(_.isFailure)) println(s"${file.getName}: failed parsing or converting file to CBOR: ${result.get.failed.get.getMessage}")
result
}
TestUtils.requireSuccessAtLeast(282, results)
TestUtils.requireSuccessAtLeast(286, results)
}

test("validate CBOR writing for standard examples") {
Expand Down Expand Up @@ -145,7 +145,7 @@ class DhallParserAndCbor2Suite extends DhallTest {
println(s"Success count: ${results.count(_.isSuccess)}\nFailure count: ${results
.count(_.isFailure)}\nCBOR expression mismatch count: ${results.filter(_.isFailure).count(_.failed.get.getMessage.contains("expression differs"))}")
results.filter(_.isFailure).map(_.failed.get.getMessage).foreach(println)
TestUtils.requireSuccessAtLeast(283, results)
TestUtils.requireSuccessAtLeast(286, results)
}

test("validate binary decoding/success") {
Expand Down

0 comments on commit db7588e

Please sign in to comment.