This is an automated email from the ASF dual-hosted git repository.
slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git
The following commit(s) were added to refs/heads/master by this push:
new cbdf30f Support empty choice branches with direct dispatch
cbdf30f is described below
commit cbdf30fbc9c447581218fb0df51cf0912d0dc757
Author: Steve Lawrence <[email protected]>
AuthorDate: Thu Mar 26 08:17:40 2020 -0400
Support empty choice branches with direct dispatch
When parsing a direct dispatch choice, it's possible one of the choice
branches is an empty sequences. This optimizes to a NadaParser which
cannot be used at runtime. So when this happens, instead use a custom
parser that does nothing, but allows for the ChoiceCombinatorParser to
have a parser to run for that branch.
The logic already exists for non-direct dispatch choices--this just uses
that same parser, though moved to a choice specific file and rename to
be consistent with the equivalent unparser and make the "choice" aspect
of it more apparent.
DAFFODIL-2306
---
.../grammar/primitives/ChoiceCombinator.scala | 10 +++++++--
.../unparsers/ChoiceAndOtherVariousUnparsers.scala | 14 ++++++------
.../processors/parsers/ElementKindParsers.scala | 15 +++++++++++++
.../daffodil/processors/parsers/Parser.scala | 22 -------------------
.../daffodil/section15/choice_groups/choice.tdml | 25 ++++++++++++++++++++++
.../section15/choice_groups/TestChoice.scala | 1 +
6 files changed, 56 insertions(+), 31 deletions(-)
diff --git
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
index abab995..14016ec 100644
---
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
+++
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
@@ -61,7 +61,7 @@ case class ChoiceCombinator(ch: ChoiceTermBase, alternatives:
Seq[Gram])
val p = alt.parser
val res =
if (p.isEmpty)
- new EmptyChoiceBranchParser(alt.context.runtimeData)
+ new ChoiceBranchEmptyParser(alt.context.runtimeData)
else p
res
}
@@ -225,7 +225,13 @@ case class ChoiceCombinator(ch: ChoiceTermBase,
alternatives: Seq[Gram])
val dispatchBranchKeyMap =
dispatchBranchKeyValueTuples.toMap.mapValues(gram => {
val isRepresented = true // FIXME: Verify is ok? Was:
gram.context.enclosingTerm.get.isRepresented
- val parser = gram.parser
+ val gramParser = gram.parser
+ val parser =
+ if (gramParser.isEmpty) {
+ new ChoiceBranchEmptyParser(gram.context.runtimeData)
+ } else {
+ gramParser
+ }
(parser, isRepresented)
})
val serializableMap: Map[String, (Parser, Boolean)] =
dispatchBranchKeyMap.map(identity)
diff --git
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
index 47cd220..6d18562 100644
---
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
+++
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ChoiceAndOtherVariousUnparsers.scala
@@ -67,15 +67,15 @@ case class ChoiceBranchMap(
}
/*
- * Sometimes choices will have an empty branch (e.g. a sequence that just has
- * an assert in it) that optimize to a NadaUnparser. NadaUnparsers should all
- * be optimized out, but the ChoiceCombinatorUnparser still expects to have
- * something in this cases. So we have a special empty branch unparser that
- * does nothing, but gives the ChoiceCombinatorUnparsering an unparse that it
- * can use.
+ * Sometimes choices have an empty branch (e.g. a sequence that just has an
+ * assert in it) that optimizes to a NadaUnparser. NadaUnparsers should all
+ * optimize out, but the ChoiceCombinatorUnparser still expects to have
+ * something in these cases. So we have a special empty branch unparser that
+ * does nothing, but gives the ChoiceCombinatorUnparser something that it can
+ * use.
*/
class ChoiceBranchEmptyUnparser(val context: RuntimeData)
- extends PrimUnparser {
+ extends PrimUnparserNoData {
override lazy val runtimeDependencies = Vector()
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
index 7bf3aab..f54c0af 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
@@ -120,6 +120,21 @@ class DynamicEscapeSchemeParser(
}
/*
+ * Sometimes choices will have an empty branch (e.g. an empty <xs:sequence />)
+ * that optimizes to a NadaParser. NadaParsers should all optimize out, but the
+ * ChoiceCombinatorParsers still expect to have a parser to use in these cases.
+ * So we have a special empty branch parser that does nothing and always
+ * succeeds, but gives the ChoiceCombinatorParsers something that they can use.
+ */
+class ChoiceBranchEmptyParser(val context: RuntimeData)
+ extends PrimParserNoData {
+
+ override lazy val runtimeDependencies = Vector()
+
+ def parse(state: PState): Unit = {}
+}
+
+/*
* dispatchBranchKeyMap: choiceBranchKey -> (Parser, hasRepresentation)
*/
diff --git
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
index 978015b..208de71 100644
---
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
+++
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
@@ -156,28 +156,6 @@ final class NadaParser(override val context: RuntimeData)
}
}
-/**
- * Explicit parser for the case of
- * <choice>
- * ....
- * ....
- * <sequence/> <!-- branch explicitly contains nothing at all -->
- * </choice>
- *
- */
-final class EmptyChoiceBranchParser(override val context: RuntimeData)
- extends PrimParserNoData {
- override def runtimeDependencies: Vector[Evaluatable[AnyRef]] = Vector()
-
- override def isEmpty = false // it's an empty one, but lying here let's us
avoid having this optimized out.
-
- override def toString = "Empty Choice Branch"
-
- override def parse(start: PState): Unit = {
- // nothing
- }
-}
-
abstract class CombinatorParser(override val context: RuntimeData)
extends Parser with CombinatorProcessor
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
index 0273105..d549e03 100644
---
a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
@@ -1754,6 +1754,18 @@ it sure is/
</xs:sequence>
</xs:group>
+ <xs:element name="dd8">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="branch" type="xs:string"
dfdl:lengthKind="explicit" dfdl:length="1" />
+ <xs:choice dfdl:choiceDispatchKey="{ ./ex:branch }">
+ <xs:sequence dfdl:choiceBranchKey="0" />
+ <xs:element ref="inty1" dfdl:choiceBranchKey="1" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
</tdml:defineSchema>
<tdml:parserTestCase name="direct_dispatch_01" root="dd1"
@@ -1965,4 +1977,17 @@ it sure is/
</tdml:infoset>
</tdml:parserTestCase>
+ <tdml:parserTestCase name="direct_dispatch_17" root="dd8"
+ model="direct_dispatch_1"
+ description="direct dispatch to empty branch">
+ <tdml:document><![CDATA[0]]></tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset xmlns:ex="http://example.com">
+ <ex:dd8>
+ <ex:branch>0</ex:branch>
+ </ex:dd8>
+ </tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
</tdml:testSuite>
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
index 3f8e1ee..51f22a0 100644
---
a/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/section15/choice_groups/TestChoice.scala
@@ -121,6 +121,7 @@ class TestChoice {
@Test def test_direct_dispatch_14() {
runnerCH.runOneTest("direct_dispatch_14") }
@Test def test_direct_dispatch_15() {
runnerCH.runOneTest("direct_dispatch_15") }
@Test def test_direct_dispatch_16() {
runnerCH.runOneTest("direct_dispatch_16") }
+ @Test def test_direct_dispatch_17() {
runnerCH.runOneTest("direct_dispatch_17") }
//@Test def test_choice_noBranch() { runnerCH.runOneTest("choice_noBranch")
} - Test consumes no data, which causes a TDMLError