mbeckerle commented on a change in pull request #88: Daffodil 1919 separators URL: https://github.com/apache/incubator-daffodil/pull/88#discussion_r206589250
########## File path: daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala ########## @@ -0,0 +1,380 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.daffodil.processors.parsers + +import org.apache.daffodil.processors.Evaluatable +import java.io.PrintWriter +import org.apache.daffodil.exceptions.UnsuppressableException +import java.io.StringWriter +import org.apache.daffodil.dsom.SchemaDefinitionDiagnosticBase +import org.apache.daffodil.exceptions.Assert +import org.apache.daffodil.processors.Success +import org.apache.daffodil.processors.SequenceRuntimeData +import org.apache.daffodil.processors.ElementRuntimeData +import org.apache.daffodil.processors.TermRuntimeData +import org.apache.daffodil.processors.Failure + +abstract class OrderedSequenceParserBase( + srd: SequenceRuntimeData, + protected val childParsers: Seq[SequenceChildParser]) + extends CombinatorParser(srd) { + override def nom = "Sequence" + + override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil + + override lazy val childProcessors: Seq[Parser] = childParsers + + /** + * Parses (1) one iteration of an array with fixed/expression occurs count. + * (2) a model group (3) a scalar element. + * + * Returns a status indicating success/failure and the nature of that success/failure. + * + * No backtracking supported. + */ + protected def parseOneWithoutPoU( + parserArg: SequenceChildParser, + trd: TermRuntimeData, + pstate: PState): ParseAttemptStatus + + /** + * Parses one iteration of an array/optional element, and returns + * a status indicating success/failure and the nature of that success/failure. + * + * Supports speculative parsing via backtracking. + */ + protected def parseOneWithPoU( + parser: RepeatingChildParser, + erd: ElementRuntimeData, + pstate: PState, + priorState: PState.Mark, + ais: GoArrayIndexStatus, + isBounded: Boolean): ParseAttemptStatus + + protected def zeroLengthSpecialChecks(pstate: PState, wasLastChildZeroLength: Boolean): Unit + + final protected def checkN(pstate: PState): Boolean = { + if (pstate.arrayPos > pstate.tunable.maxOccursBounds) { + PE(pstate, "Occurs count %s exceeds implementation maximum of %s.", pstate.arrayPos, pstate.tunable.maxOccursBounds) + false + } else true + } + + /** + * This parse method is used for both separated and unseparated sequences. + */ + override protected def parse(pstate: PState): Unit = { + val children = childParsers + + var scpIndex = 0 + pstate.mpstate.groupIndexStack.push(1L) // one-based indexing + + val limit = children.length + + var wasLastChildZeroLength = false + + // + // This loop iterates over the children terms of the sequence + // + while ((scpIndex < limit) && (pstate.processorStatus eq Success)) { + val child = children(scpIndex) + child match { + case parser: RepeatingChildParser => { + // + // The sequence child is an array/repeating element (or ooptional + // element as the runtime doesn't distinguish them.) + // + // + val min = parser.minRepeats(pstate) + val max = parser.maxRepeats(pstate) + val isBounded = parser.isBoundedMax(max) + val erd = parser.trd.asInstanceOf[ElementRuntimeData] + + parser.startArray(pstate) + + // + // There are two kinds of loops. Arrays which have points of uncertainty (PoU) + // where speculative parsing is used to determine how many occurrences, + // and specified-number of occurrences, where a number is known or is computed. + // + parser.hasPoU match { + case true => { + // + // This case for array/optionals where the number of occurences is + // determined by speculative parsing. OCK=implicit with min/maxOccurs + // different, or OCK=parsed. + // + + // + // The beforeArrayState will be assigned the priorState before the whole array + // This is the same object as the priorState before the first + // occurrence. + // + var beforeArrayState: PState.Mark = null + + var resultOfTry: ParseAttemptStatus = ParseAttemptStatus.Uninitialized + + var ais: ArrayIndexStatus = null + var goAIS: GoArrayIndexStatus = null + + var isFirstIteration = true + + while ({ + ais = parser.arrayIndexStatus(min, max, pstate, resultOfTry) + ais match { + case go: GoArrayIndexStatus => { goAIS = go; true } + case _ => false + } + }) { + + // + // Saved state before an individual occurrence. + // + // On the first iteration this is the same as the beforeArrayState + // and by "same" we mean same object in the 'eq' sense. + // + // If we are in a second or subsequent iteration then an invariant is + // that this is NOT the same as the beforeArrayState. + // + // These should not leak as we iterate the occurrences. + // The lifetime of these is the parse attempt for a single occurrence + // only. + // + val priorState = + if (isFirstIteration) { + beforeArrayState = pstate.mark("before all occurrences") + beforeArrayState + } else { + pstate.mark("before second/subsequent occurrence") + } + + checkN(pstate) // check if arrayIndex exceeds tunable limit. + + var markLeakCausedByException = false + var wasThrow = true + try { + resultOfTry = + parseOneWithPoU(parser, erd, pstate, priorState, goAIS, isBounded) + wasThrow = false + // + // Now we handle the result of the parse attempt. + // + + // check for consistency - failure comes with a PE in the PState. + Assert.invariant((pstate.processorStatus eq Success) || + resultOfTry.isInstanceOf[FailedParseAttemptStatus]) + + resultOfTry match { + // + // These statuses for whole array/optional are not used + // for PoU occurrences. + // + case ParseAttemptStatus.Failed_EntireArray | + ParseAttemptStatus.Success_EndOfArray => + Assert.invariantFailed("not valid return status for a PoU array/optional.") + + case ParseAttemptStatus.Success_SkippedSeparator => { + // + // In the case of separated sequences when we skip just the separator, + // the parseWithOnePoU method has handled the reset to priorState + // and advance past the separator. It has to, as it knows + // how long the separator was. + // + wasLastChildZeroLength = true + } + case _: SuccessParseAttemptStatus => { + pstate.mpstate.moveOverOneGroupIndexOnly() + // + // Here we leave beforeArrayState alone. May still + // need it later. + // + wasLastChildZeroLength = resultOfTry eq ParseAttemptStatus.Success_ZeroLength + } + + case ParseAttemptStatus.Failed_WithDiscriminatorSet => { + // Just allow the failure to propagate. + } + + case ParseAttemptStatus.Failed_SpeculativeParse => { + // We failed. + goAIS match { + case ArrayIndexStatus.Required => { + // + // Did we reach minOccurs? I.e., did we fail on a required element? + // if so, then we failed the whole array + // + // Grab the cause, restore the state prior to the whole array, + // then re-assert the failure cause. + // + val Failure(cause) = pstate.processorStatus + + // + // Discarding has to be done before resetting of an outer/earlier mark + // because the InputDataStream marks are unwound in a stack manner. I.e., resetting + // to an earlier one implicitly discards any nested within that. Review comment: "nested within" - really "more recent" or "newer" ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
