| package edu.illinois.ncsa.daffodil.dsom |
| |
| /* Copyright (c) 2012-2013 Tresys Technology, LLC. All rights reserved. |
| * |
| * Developed by: Tresys Technology, LLC |
| * http://www.tresys.com |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy of |
| * this software and associated documentation files (the "Software"), to deal with |
| * the Software without restriction, including without limitation the rights to |
| * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is furnished to do |
| * so, subject to the following conditions: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimers. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimers in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * 3. Neither the names of Tresys Technology, nor the names of its contributors |
| * may be used to endorse or promote products derived from this Software |
| * without specific prior written permission. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE |
| * SOFTWARE. |
| */ |
| |
| import scala.xml.Node |
| import edu.illinois.ncsa.daffodil.exceptions.Assert |
| import edu.illinois.ncsa.daffodil.grammar._ |
| import edu.illinois.ncsa.daffodil.schema.annotation.props._ |
| import edu.illinois.ncsa.daffodil.schema.annotation.props.gen._ |
| import edu.illinois.ncsa.daffodil.dsom.oolag.OOLAG._ |
| import edu.illinois.ncsa.daffodil.util._ |
| import edu.illinois.ncsa.daffodil.util.Misc.bytes2Hex |
| import com.ibm.icu.text.NumberFormat |
| import edu.illinois.ncsa.daffodil.processors._ |
| import java.math.BigInteger |
| |
| trait GrammarMixin { |
| protected val NYI = false // our flag for Not Yet Implemented |
| } |
| |
| trait InitiatedTerminatedMixin |
| extends GrammarMixin |
| with AnnotatedMixin |
| with DelimitedRuntimeValuedPropertiesMixin { self: Term => |
| |
| lazy val parentSaysInitiatedContent = { |
| val parentSays = self.immediatelyEnclosingModelGroup match { |
| case Some(s) if (s.initiatedContent == YesNo.Yes) => true |
| case _ => false |
| } |
| parentSays |
| } |
| |
| lazy val hasInitiator = { |
| val hasOne = initiator.isKnownNonEmpty |
| if (parentSaysInitiatedContent) |
| schemaDefinition(hasOne, "Enclosing group has initiatedContent='yes', but initiator is not defined.") |
| hasOne |
| } |
| |
| lazy val hasTerminator = terminator.isKnownNonEmpty |
| |
| lazy val initiatorDiscriminator = Prod("initiatorDiscriminator", this, parentSaysInitiatedContent, InitiatedContent(this)) |
| |
| lazy val initiatorRegion = Prod("initiatorRegion", this, hasInitiator, initiatorItself ~ initiatorDiscriminator) |
| lazy val initiatorItself = { |
| if (initiator.isConstant) StaticInitiator(this) |
| else DynamicInitiator(this) |
| } |
| |
| lazy val terminatorRegion = Prod("terminatorRegion", this, hasTerminator, |
| if (terminator.isConstant) StaticTerminator(this) |
| else DynamicTerminator(this)) |
| |
| lazy val escapeScheme: Option[DFDLEscapeScheme] = { |
| val er = getPropertyOption("escapeSchemeRef") |
| er match { |
| case None => None |
| case Some(qName) => { |
| |
| if (qName.length() == 0) { |
| None |
| } else { |
| val (nsURI, name) = formatAnnotation.resolveQName(qName) |
| val defES = schemaSet.getDefineEscapeScheme(nsURI, name) |
| defES match { |
| case None => SDE("Define Escape Scheme Not Found") |
| case Some(es) => Some(es.escapeScheme) |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| trait AlignedMixin { self: Term => |
| lazy val leadingSkipRegion = Prod("leadingSkipRegion", this, LeadingSkipRegion(this)) |
| lazy val trailingSkipRegion = Prod("trailingSkipRegion", this, TrailingSkipRegion(this)) |
| lazy val alignmentFill = Prod("alignmentFill", this, AlignmentFill(this)) |
| } |
| |
| ///////////////////////////////////////////////////////////////// |
| // Elements System |
| ///////////////////////////////////////////////////////////////// |
| |
| trait ElementBaseGrammarMixin |
| extends InitiatedTerminatedMixin |
| with AlignedMixin |
| with HasStatementsGrammarMixin { self: ElementBase => |
| // |
| // This silly redundancy where the variable name has to also be passed as a string, |
| // is, by the way, a good reason Scala needs real Lisp-style macros, that can take an argument and |
| // turn it into a type/class, object, def, or val/var name, as well as a string, etc. |
| // |
| |
| lazy val parsedNil = Prod("parsedNil", this, NYI && isNillable && nilKind == NilKind.LogicalValue, |
| nilElementInitiator ~ LogicalNilValue(this) ~ nilElementTerminator) |
| |
| lazy val parsedValue = { |
| val res = Prod("parsedValue", this, initiatorRegion ~ allowedValue ~ terminatorRegion) |
| res |
| } |
| |
| def allowedValue: Prod // provided by LocalElementBase for array considerations, and GlobalElementDecl - scalar only |
| |
| // lazy val explicitLengthBinary = Prod("explicitLengthBinary", this, !isFixedLength, |
| // lengthUnits match { |
| // case LengthUnits.Bytes => BinaryExplicitLengthInBytes(this) |
| // case LengthUnits.Characters => schemaDefinitionError("Binary data elements cannot have lengthUnits='Character'.") |
| // case LengthUnits.Bits => BinaryExplicitLengthInBits(this) |
| // }) |
| // |
| // lazy val binaryValueLength = binaryValueLength_.value |
| // lazy val binaryValueLength_ = LV { |
| // val res = Prod("BinaryValueLength", this, lengthKind match { |
| // case LengthKind.Explicit => explicitLengthBinary |
| // case LengthKind.Delimited => Assert.notYetImplemented() // Binary Data delimiters aren't supported TODO: Should we? |
| // case LengthKind.Pattern => schemaDefinitionError("Binary data elements cannot have lengthKind='Pattern'.") |
| // case LengthKind.Implicit => Assert.notYetImplemented() // TODO: Get size from xs:type |
| // case _ => Assert.notYetImplemented() |
| // }) |
| // res |
| // } |
| |
| // Length is in bits, (size would be in bytes) (from DFDL Spec 12.3.3) |
| lazy val implicitBinaryLengthInBits: Long = primType.myPrimitiveType match { |
| case PrimType.Byte | PrimType.UByte => 8 |
| case PrimType.Short | PrimType.UShort => 16 |
| case PrimType.Float | PrimType.Int | PrimType.UInt | PrimType.Boolean => 32 |
| case PrimType.Double | PrimType.Long | PrimType.ULong => 64 |
| case _ => schemaDefinitionError("Size of binary data '" + primType.name + "' cannot be determined implicitly.") |
| } |
| |
| lazy val binaryNumberKnownLengthInBits: Long = lengthKind match { |
| case LengthKind.Implicit => implicitBinaryLengthInBits |
| case LengthKind.Explicit if (length.isConstant) => { |
| val lengthFromProp = length.constantAsLong |
| val nbits = lengthUnits match { |
| case LengthUnits.Bits => lengthFromProp |
| case LengthUnits.Bytes => lengthFromProp * 8 |
| case LengthUnits.Characters => SDE("The lengthUnits for binary numbers must be either 'bits' or 'bytes'. Not 'characters'.") |
| } |
| nbits |
| } |
| case LengthKind.Explicit => -1 // means must be computed at runtime. |
| case LengthKind.Delimited => schemaDefinitionError("Binary data elements cannot have lengthKind='delimited'.") |
| case LengthKind.Pattern => schemaDefinitionError("Binary data elements cannot have lengthKind='pattern'.") |
| case LengthKind.Prefixed => subsetError("lengthKind='prefixed' not yet supported.") |
| case LengthKind.EndOfParent => schemaDefinitionError("Binary data elements cannot have lengthKind='endOfParent'.") |
| } |
| |
| lazy val fixedLengthString = Prod("fixedLengthString", this, isFixedLength, |
| (lengthUnits, knownEncodingIsFixedWidth) match { |
| case (LengthUnits.Bytes, true) => StringFixedLengthInBytesFixedWidthCharacters(this, fixedLength) // TODO: make sure it divides evenly. |
| //case (LengthUnits.Bytes, true) => StringFixedLengthInBytes(this, fixedLength / knownEncodingWidth) // TODO: make sure it divides evenly. |
| case (LengthUnits.Bytes, false) => StringFixedLengthInBytesVariableWidthCharacters(this, fixedLength) |
| case (LengthUnits.Characters, true) => { |
| // |
| // we deal with the fact that some encodings have characters taking up smaller than |
| // a full byte. E.g., encoding='US-ASCII-7-bit-packed' are 7-bits packed with no unused |
| // bits |
| // |
| val lengthInBits = fixedLength * knownEncodingWidthInBits |
| val lengthInBytes = lengthInBits / 8 |
| val hasWholeBytesOnly = (lengthInBits % 8) == 0 |
| if (hasWholeBytesOnly) |
| StringFixedLengthInBytesFixedWidthCharacters(this, lengthInBytes) |
| else |
| StringFixedLengthInBytesFixedWidthCharacters(this, lengthInBytes + 1) // 1 more for fragment byte |
| } |
| // |
| // The string may be "fixed" length, but a variable-width charset like utf-8 means that N characters can take anywhere from N to |
| // 4*N bytes. So it's not really fixed width. We'll have to parse the string to determine the actual length. |
| case (LengthUnits.Characters, false) => StringFixedLengthInVariableWidthCharacters(this, fixedLength) |
| case (LengthUnits.Bits, _) => notYetImplemented("lengthUnits='bits' for type " + typeDef) |
| case _ => Assert.invariantFailed("all cases should have been exhausted.") |
| }) |
| |
| lazy val implicitLengthString = Prod("implicitLengthString", this, hasSpecifiedLength, |
| (lengthUnits, knownEncodingIsFixedWidth) match { |
| case (LengthUnits.Bytes, true) => StringFixedLengthInBytesFixedWidthCharacters(this, facetMaxLength) // TODO: make sure it divides evenly. |
| //case (LengthUnits.Bytes, true) => StringFixedLengthInBytes(this, fixedLength / knownEncodingWidth) // TODO: make sure it divides evenly. |
| case (LengthUnits.Bytes, false) => StringFixedLengthInBytesVariableWidthCharacters(this, facetMaxLength) |
| case (LengthUnits.Characters, true) => { |
| // |
| // we deal with the fact that some encodings have characters taking up smaller than |
| // a full byte. E.g., encoding='US-ASCII-7-bit-packed' are 7-bits packed with no unused |
| // bits |
| // |
| val lengthInBits = facetMaxLength * knownEncodingWidthInBits |
| val lengthInBytes = lengthInBits / 8 |
| val hasWholeBytesOnly = (lengthInBits % 8) == 0 |
| if (hasWholeBytesOnly) |
| StringFixedLengthInBytesFixedWidthCharacters(this, lengthInBytes) |
| else |
| StringFixedLengthInBytesFixedWidthCharacters(this, lengthInBytes + 1) // 1 more for fragment byte |
| } |
| // |
| // The string may be "fixed" length, but a variable-width charset like utf-8 means that N characters can take anywhere from N to |
| // 4*N bytes. So it's not really fixed width. We'll have to parse the string to determine the actual length. |
| case (LengthUnits.Characters, false) => StringFixedLengthInVariableWidthCharacters(this, facetMaxLength) |
| case (LengthUnits.Bits, _) => SDE("Strings with lengthKind='implicit' may not have lengthUnits='bits'") |
| }) |
| |
| lazy val variableLengthString = Prod("variableLengthString", this, !isFixedLength, |
| (lengthUnits, knownEncodingIsFixedWidth) match { |
| //case (LengthUnits.Bytes, true) => StringExplicitLengthInBytes(this) |
| //case (LengthUnits.Bytes, false) => |
| // notYetImplemented("lengthKind='explicit' and lengthUnits='bytes' with non-fixed-width or potentially non-fixed-width encoding='%s'.", this.encodingRaw) |
| //// StringExplicitLengthInBytesVariableWidthCharacters(this) |
| //case (LengthUnits.Characters, _) => |
| // notYetImplemented("lengthKind='explicit' and lengthUnits='characters'") |
| //// Above, keep in mind fixed length but variable-width encoding means variable width. |
| //// The string may be "fixed" length, but a variable-width charset like utf-8 means that N characters can take anywhere from N to |
| //// 4*N bytes. So it's not really fixed width. We'll have to parse the string to determine the actual length. |
| //case (LengthUnits.Bits, _) => |
| // SDE("lengthKind='explicit' and lengthUnits='bits' for type %s", this.typeDef) |
| case (LengthUnits.Bytes, true) => StringVariableLengthInBytes(this) |
| case (LengthUnits.Bytes, false) => StringVariableLengthInBytesVariableWidthCharacters(this) |
| case (LengthUnits.Characters, true) => StringVariableLengthInBytes(this) |
| // The string may be "fixed" length, but a variable-width charset like utf-8 means that N characters can take anywhere from N to |
| // 4*N bytes. So it's not really fixed width. We'll have to parse the string to determine the actual length. |
| case (LengthUnits.Characters, false) => StringVariableLengthInVariableWidthCharacters(this) |
| case (LengthUnits.Bits, _) => SDE("lengthKind='explicit' and lengthUnits='bits' for type %s", this.typeDef) |
| }) |
| |
| //lazy val stringDelimitedEndOfData = Prod("stringDelimitedEndOfData", this, StringDelimitedEndOfData(this)) |
| lazy val stringDelimitedEndOfDataStatic = Prod("stringDelimitedEndOfDataStatic", this, StringDelimitedEndOfDataStatic(this)) |
| lazy val stringDelimitedEndOfDataDynamic = Prod("stringDelimitedEndOfDataDynamic", this, StringDelimitedEndOfDataDynamic(this)) |
| lazy val stringPatternMatched = Prod("stringPatternMatched", this, StringPatternMatched(this)) |
| |
| lazy val stringValue = stringValue_.value |
| private val stringValue_ = LV('stringValue) { |
| val res = Prod("stringValue", this, lengthKind match { |
| case LengthKind.Explicit if isFixedLength => fixedLengthString |
| case LengthKind.Explicit => variableLengthString |
| //case LengthKind.Delimited => stringDelimitedEndOfData |
| case LengthKind.Delimited if this.hasExpressionsInTerminatingMarkup => stringDelimitedEndOfDataDynamic |
| case LengthKind.Delimited => stringDelimitedEndOfDataStatic |
| case LengthKind.Pattern => stringPatternMatched |
| case LengthKind.Implicit => implicitLengthString |
| case _ => SDE("Unimplemented lengthKind %s", lengthKind) |
| }) |
| res |
| } |
| |
| // lazy val binaryByte = Prod("binaryByte", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryShort = Prod("binaryShort", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryLong = Prod("binaryLong", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryInteger = Prod("binaryInteger", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryUnsignedInt = Prod("binaryInt", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryUnsignedByte = Prod("binaryByte", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryUnsignedShort = Prod("binaryShort", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| // |
| // lazy val binaryUnsignedLong = Prod("binaryLong", this, representation == Representation.Binary, |
| // regularBinaryRepInt | bcdInt | packedInt) |
| |
| // lazy val regularBinaryRepInt = Prod("regularBinaryRepInt", this, |
| // binaryNumberRep == BinaryNumberRep.Binary, lengthKind match { |
| // case LengthKind.Implicit => { |
| // if (byteOrder.isConstant) { |
| // val javaByteOrder = ByteOrder(byteOrder.constantAsString, this) match { |
| // case ByteOrder.BigEndian => java.nio.ByteOrder.BigEndian |
| // case ByteOrder.LittleEndian => java.nio.ByteOrder.LittleEndian |
| // } |
| // Regular32bitIntPrim(this, javaByteOrder) |
| // else Assert.notYetImplemented() // Dynamic byte order not implemented |
| // } |
| // case _ => Assert.notYetImplemented() // binary number length kinds other than implicit not implemented |
| // }) |
| |
| // lazy val bcdInt = Prod("bcdInt", this, |
| // binaryNumberRep == BinaryNumberRep.Bcd, BCDIntPrim(this)) |
| // lazy val packedInt = Prod("packedInt", this, |
| // binaryNumberRep == BinaryNumberRep.Packed, PackedIntPrim(this)) |
| |
| // TODO: Handle the zonedTextXXX possibilities |
| lazy val textInt = Prod("textInt", this, representation == Representation.Text, |
| standardTextInt | zonedTextInt) |
| |
| lazy val textByte = Prod("textByte", this, representation == Representation.Text, |
| standardTextByte | zonedTextInt) |
| |
| lazy val textShort = Prod("textShort", this, representation == Representation.Text, |
| standardTextShort | zonedTextInt) |
| |
| lazy val textLong = Prod("textLong", this, representation == Representation.Text, |
| standardTextLong | zonedTextInt) |
| |
| lazy val textInteger = Prod("textInteger", this, representation == Representation.Text, |
| standardTextInteger | zonedTextInt) |
| |
| lazy val textUnsignedInt = Prod("textUnsignedInt", this, representation == Representation.Text, |
| standardTextUnsignedInt | zonedTextInt) |
| |
| lazy val textUnsignedByte = Prod("textUnsignedByte", this, representation == Representation.Text, |
| standardTextUnsignedByte | zonedTextInt) |
| |
| lazy val textUnsignedShort = Prod("textUnsignedShort", this, representation == Representation.Text, |
| standardTextUnsignedShort | zonedTextInt) |
| |
| lazy val textUnsignedLong = Prod("textUnsignedLong", this, representation == Representation.Text, |
| standardTextUnsignedLong | zonedTextInt) |
| |
| // |
| // We could now break it down by lengthKind, and have specialized primitives |
| // depending on the length kind. |
| // |
| lazy val standardTextInteger = Prod("standardTextInteger", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextIntegerPrim(this)) |
| lazy val standardTextLong = Prod("standardTextLong", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextLongPrim(this)) |
| lazy val standardTextInt = Prod("standardTextInt", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextIntPrim(this)) |
| lazy val standardTextShort = Prod("standardTextShort", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextShortPrim(this)) |
| lazy val standardTextByte = Prod("standardTextByte", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextBytePrim(this)) |
| lazy val standardTextUnsignedLong = Prod("standardTextUnsignedLong", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextUnsignedLongPrim(this)) |
| lazy val standardTextUnsignedInt = Prod("standardTextUnsignedInt", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextUnsignedIntPrim(this)) |
| lazy val standardTextUnsignedShort = Prod("standardTextUnsignedShort", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextUnsignedShortPrim(this)) |
| lazy val standardTextUnsignedByte = Prod("standardTextUnsignedByte", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextUnsignedBytePrim(this)) |
| lazy val zonedTextInt = Prod("zonedTextInt", this, |
| textNumberRep == TextNumberRep.Zoned, ZonedTextIntPrim(this)) |
| |
| // lazy val binaryDouble = Prod("binaryDouble", this, representation == Representation.Binary, |
| // ieeeBinaryRepDouble | ibm390HexBinaryRepDouble) |
| |
| lazy val textDouble = Prod("textDouble", this, representation == Representation.Text, |
| standardTextDouble | zonedTextDouble) |
| |
| // lazy val ieeeBinaryRepDouble = Prod("ieeeBinaryRepDouble", this, |
| // { |
| // val bfr = binaryFloatRep |
| // val res = bfr.isConstant && |
| // BinaryFloatRep(bfr.constantAsString, this) == BinaryFloatRep.Ieee |
| // res |
| // }, |
| // lengthKind match { |
| // case LengthKind.Implicit => { |
| // if (byteOrder.isConstant) ByteOrder(byteOrder.constantAsString, this) match { |
| // case ByteOrder.BigEndian => BigEndianDoublePrim(this) |
| // case ByteOrder.LittleEndian => LittleEndianDoublePrim(this) |
| // } |
| // else Assert.notYetImplemented() |
| // } |
| // case _ => Assert.notYetImplemented() |
| // }) |
| |
| lazy val ibm390HexBinaryRepDouble = Prod("ibm390HexBinaryRepDouble", this, |
| binaryFloatRep.isConstant && |
| binaryFloatRep.constantAsString == BinaryFloatRep.Ibm390Hex.toString, |
| subsetError("ibm390Hex not supported")) |
| |
| lazy val standardTextDouble = Prod("standardTextDouble", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextDoublePrim(this)) |
| |
| lazy val zonedTextDouble = Prod("zonedTextDouble", this, |
| textNumberRep == TextNumberRep.Zoned, subsetError("Zoned not supported for float and double")) |
| |
| // lazy val binaryFloat = Prod("binaryFloat", this, representation == Representation.Binary, |
| // ieeeBinaryRepFloat | ibm390HexBinaryRepFloat) |
| |
| lazy val textFloat = Prod("textFloat", this, representation == Representation.Text, |
| standardTextFloat | zonedTextFloat) |
| |
| // lazy val ieeeBinaryRepFloat = Prod("ieeeBinaryRepFloat", this, |
| // { |
| // val bfr = binaryFloatRep |
| // val res = bfr.isConstant && |
| // BinaryFloatRep(bfr.constantAsString, this) == BinaryFloatRep.Ieee |
| // res |
| // }, |
| // lengthKind match { |
| // case LengthKind.Implicit => { |
| // if (byteOrder.isConstant) ByteOrder(byteOrder.constantAsString, this) match { |
| // case ByteOrder.BigEndian => BigEndianFloatPrim(this) |
| // case ByteOrder.LittleEndian => LittleEndianFloatPrim(this) |
| // } |
| // else Assert.notYetImplemented() |
| // } |
| // case _ => Assert.notYetImplemented() |
| // }) |
| // |
| // lazy val ibm390HexBinaryRepFloat = Prod("ibm390HexBinaryRepFloat", this, |
| // binaryFloatRep.isConstant && |
| // binaryFloatRep.constantAsString == BinaryFloatRep.Ibm390Hex.toString, |
| // subsetError("ibm390Hex not supported")) |
| |
| lazy val standardTextFloat = Prod("standardTextFloat", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextFloatPrim(this)) |
| |
| lazy val zonedTextFloat = Prod("zonedTextFloat", this, |
| textNumberRep == TextNumberRep.Zoned, subsetError("Zoned not supported for float and double")) |
| |
| lazy val textDate = Prod("textDate", this, representation == Representation.Text, |
| standardTextDate | zonedTextDate) |
| lazy val textTime = Prod("textTime", this, representation == Representation.Text, |
| standardTextTime | zonedTextTime) |
| lazy val textDateTime = Prod("textDateTime", this, representation == Representation.Text, |
| standardTextDateTime | zonedTextDateTime) |
| |
| lazy val standardTextDate = Prod("standardTextDate", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextDatePrim(this)) |
| lazy val standardTextTime = Prod("standardTextTime", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextTimePrim(this)) |
| lazy val standardTextDateTime = Prod("standardTextDateTime", this, |
| textNumberRep == TextNumberRep.Standard, stringValue ~ ConvertTextDateTimePrim(this)) |
| |
| lazy val zonedTextDate = Prod("zonedTextDate", this, |
| textNumberRep == TextNumberRep.Zoned, subsetError("Zoned not supported for date")) |
| lazy val zonedTextTime = Prod("zonedTextDate", this, |
| textNumberRep == TextNumberRep.Zoned, subsetError("Zoned not supported for time")) |
| lazy val zonedTextDateTime = Prod("zonedTextDate", this, |
| textNumberRep == TextNumberRep.Zoned, subsetError("Zoned not supported for dateTime")) |
| |
| // shorthand |
| lazy val primType = { |
| val res = typeDef.asInstanceOf[SimpleTypeBase].primitiveType |
| res |
| } |
| |
| // lazy val value = Prod("value", this, isSimpleType, |
| // // TODO: Consider issues with matching a stopValue. Can't say isScalar here because |
| // // This gets used for array contents also. |
| // { |
| // primType.name match { |
| // case "string" => stringValue |
| // case _ => { |
| // val res = representation match { |
| // case Representation.Binary => binaryValue |
| // case Representation.Text => textValue |
| // } |
| // res |
| // } |
| // } |
| // }) |
| |
| lazy val value = Prod("value", this, isSimpleType, |
| // TODO: Consider issues with matching a stopValue. Can't say isScalar here because |
| // this gets used for array contents also. |
| { |
| primType.myPrimitiveType match { |
| case PrimType.String => stringValue |
| case _ => { |
| val res = representation match { |
| case Representation.Binary => binaryValue |
| case Representation.Text => textValue |
| } |
| res |
| } |
| } |
| }) |
| |
| // This is the right name that the DFDL property should have had! |
| lazy val binaryIntRep = { |
| subset(binaryNumberRep == BinaryNumberRep.Binary, "binaryNumberRep='%s' is unsupported. Only 'binary' is supported.", binaryNumberRep.toString) |
| binaryNumberRep |
| } |
| |
| lazy val staticBinaryFloatRep = { |
| subset(binaryFloatRep.isConstant, "Dynamic binaryFloatRep is not supported.") |
| BinaryFloatRep(binaryFloatRep.constantAsString, this) |
| } |
| |
| lazy val binary = { |
| subset(lengthKind == LengthKind.Explicit, "Currently only lengthKind='explicit' is supported.") |
| LengthKind(lengthKind.toString(), this) |
| } |
| |
| val bin = BinaryNumberRep.Binary // shorthands for table dispatch |
| val ieee = BinaryFloatRep.Ieee |
| type BO = java.nio.ByteOrder |
| |
| lazy val zero = new BigInteger("0") |
| lazy val two = new BigInteger("2") |
| lazy val maximumUnsignedLong = two.pow(64).subtract(new BigInteger("1")) |
| |
| lazy val binaryValue: Gram = { |
| Assert.invariant(primType.myPrimitiveType != PrimType.String) |
| |
| subset(byteOrder.isConstant, "Dynamic byte order is not currently supported.") |
| |
| // We have to dispatch carefully here. We cannot force evaluation of properties |
| // that may not be necessary. E.g., float does not need property binaryNumberRep, so |
| // if our dispatch table uses that, it will create a false dependency on the property |
| // being defined. |
| // The DFDL spec has a section where it gives the precedence order of properties. |
| // This is in the spirit of that section. |
| val res: Gram = primType.myPrimitiveType match { |
| |
| // case PrimType.HexBinary => |
| // (primType.myPrimitiveType, binary) match { // TODO: Only takes explicit length |
| // case (PrimType.HexBinary, b) => new BinaryNumberBase[Array[Byte]](this, this.length.constantAsLong) { |
| // def getNum(bp : Long, in : InStream, bo : BO) = { |
| // // FIXME: size constraints, overflow |
| // in.getByteArray(bp, bo, length.constantAsLong.asInstanceOf[Int]) |
| // } |
| // override def getNum(num : Number) = null //FIXME |
| // protected override val GramName = "hexBinary" |
| // protected override val GramDescription = "Hex Binary" |
| // protected override def numFormat = NumberFormat.getIntegerInstance() |
| // protected override def isInt = true |
| // } |
| // case _ => Assert.impossibleCase() |
| // } |
| |
| case PrimType.Byte | PrimType.Short | PrimType.Int | PrimType.Long | PrimType.Integer => { |
| Assert.invariant(binaryIntRep == bin) |
| binaryNumberKnownLengthInBits match { |
| case -1 => new SignedRuntimeLengthRuntimeByteOrderBinaryNumber(this) |
| case _ => new SignedKnownLengthRuntimeByteOrderBinaryNumber(this, binaryNumberKnownLengthInBits) |
| } |
| } |
| |
| case PrimType.UByte | PrimType.UShort | PrimType.UInt | PrimType.ULong => { |
| Assert.invariant(binaryIntRep == bin) |
| binaryNumberKnownLengthInBits match { |
| case -1 => new UnsignedRuntimeLengthRuntimeByteOrderBinaryNumber(this) |
| case _ => new UnsignedKnownLengthRuntimeByteOrderBinaryNumber(this, binaryNumberKnownLengthInBits) |
| } |
| } |
| |
| case PrimType.Double | PrimType.Float => |
| (primType.myPrimitiveType, binaryNumberKnownLengthInBits, staticBinaryFloatRep) match { |
| case (_, -1, BinaryFloatRep.Ieee) => SDE("Floating point binary numbers may not have runtime-specified lengths.") |
| case (PrimType.Float, 32, BinaryFloatRep.Ieee) => new FloatKnownLengthRuntimeByteOrderBinaryNumber(this, 32) |
| case (PrimType.Float, n, BinaryFloatRep.Ieee) => SDE("binary xs:float must be 32 bits. Length in bits was %s.", n) |
| case (PrimType.Double, 64, BinaryFloatRep.Ieee) => new DoubleKnownLengthRuntimeByteOrderBinaryNumber(this, 64) |
| case (PrimType.Double, n, BinaryFloatRep.Ieee) => SDE("binary xs:double must be 64 bits. Length in bits was %s.", n) |
| case (_, _, floatRep) => subsetError("binaryFloatRep='%s' not supported. Only binaryFloatRep='ieee'", floatRep.toString) |
| } |
| |
| // (primType.myPrimitiveType, staticBinaryFloatRep) match { |
| // case (PrimType.Double, ieee) => new BinaryNumber[Double](this, 64) { |
| // Assert.invariant(staticBinaryFloatRep == BinaryFloatRep.Ieee) |
| // def getNum(bp : Long, in : InStream, bo : BO) = in.getDouble(bp, bo) |
| // override def getNum(num : Number) = num.doubleValue |
| // protected override val GramName = "double" |
| // protected override val GramDescription = "Double" |
| // protected override def numFormat = NumberFormat.getNumberInstance() // .getScientificInstance() Note: scientific doesn't allow commas as grouping separators. |
| // protected override def isInt = false |
| // } |
| // case (PrimType.Float, ieee) => new BinaryNumber[Float](this, 32) { |
| // Assert.invariant(staticBinaryFloatRep == BinaryFloatRep.Ieee) |
| // def getNum(bp : Long, in : InStream, bo : BO) = in.getFloat(bp, bo) |
| // override def getNum(num : Number) = num.floatValue |
| // protected override val GramName = "float" |
| // protected override val GramDescription = "Float" |
| // protected override def numFormat = NumberFormat.getNumberInstance() // .getScientificInstance() Note: scientific doesn't allow commas as grouping separators. |
| // protected override def isInt = false |
| // } |
| // case (_, floatRep) => subsetError("binaryFloatRep='%s' not supported. Only binaryFloatRep='ieee'", floatRep.toString) |
| // } |
| case _ => schemaDefinitionError("Unrecognized primitive type: " + primType.name) |
| } |
| res |
| } |
| |
| lazy val textValue: Gram = { |
| Assert.invariant(primType.myPrimitiveType != PrimType.String) |
| val res = primType.myPrimitiveType match { |
| |
| case PrimType.Int => textInt |
| case PrimType.Byte => textByte |
| case PrimType.Short => textShort |
| case PrimType.Long => textLong |
| case PrimType.Integer => textInteger |
| case PrimType.UInt => textUnsignedInt |
| case PrimType.UByte => textUnsignedByte |
| case PrimType.UShort => textUnsignedShort |
| case PrimType.ULong => textUnsignedLong |
| case PrimType.Double => textDouble |
| case PrimType.Float => textFloat |
| case PrimType.HexBinary => notYetImplemented("textValue: hexBinary") |
| case PrimType.Boolean => notYetImplemented("textValue: boolean") |
| case PrimType.Date => textDate |
| case PrimType.Time => textTime |
| case PrimType.DateTime => textDateTime |
| case _ => schemaDefinitionError("Unrecognized primitive type: " + primType.name) |
| } |
| res |
| } |
| |
| lazy val empty = Prod("empty", this, NYI && emptyIsAnObservableConcept, emptyRepresentation) |
| |
| lazy val emptyRepresentation = Prod("emptyRepresentation", this, |
| simpleOrNonImplicitComplexEmpty | complexImplicitEmpty) |
| |
| lazy val simpleOrNonImplicitComplexEmpty = Prod("simpleOrNonImplicitComplexEmpty", this, |
| NYI && isSimpleType || isComplexType && lengthKind != LengthKind.Implicit, |
| emptyElementInitiator ~ emptyElementTerminator) |
| |
| /** |
| * This is about the case where we take an empty, parse a complex type recursively from it |
| * and potentially succeed. |
| */ |
| lazy val complexImplicitEmpty = Prod("complexImplicitEmpty", this, NYI && |
| isComplexType && lengthKind == LengthKind.Implicit, |
| SaveInputStream(this) ~ SetEmptyInputStream(this) ~ elementComplexType.mainGrammar ~ |
| RestoreInputStream(this) ~ emptyElementTerminator) |
| |
| lazy val emptyDefaulted = Prod("emptyDefaulted", this, |
| isDefaultable && emptyIsAnObservableConcept, |
| empty ~ TheDefaultValue(this)) |
| |
| lazy val nilElementInitiator = Prod("nilElementInitiator", this, hasNilValueInitiator, |
| if (initiator.isConstant) StaticInitiator(this) else DynamicInitiator(this)) |
| lazy val nilElementTerminator = Prod("nilElementTerminator", this, hasNilValueTerminator, |
| if (terminator.isConstant) StaticTerminator(this) else DynamicTerminator(this)) |
| |
| lazy val emptyElementInitiator = Prod("emptyElementInitiator", this, NYI && hasEmptyValueInitiator, EmptyGram) |
| lazy val emptyElementTerminator = Prod("emptyElementTerminator", this, NYI && hasEmptyValueTerminator, EmptyGram) |
| |
| lazy val complexContent = Prod("complexContent", this, isComplexType, |
| initiatorRegion ~ elementComplexType.mainGrammar ~ terminatorRegion) |
| |
| lazy val nilLit = { |
| Prod("nilLit", this, |
| isNillable && nilKind == NilKind.LiteralValue, |
| nilElementInitiator ~ { |
| // if (representation != Representation.Text) this.SDE("LiteralValue Nils require representation='text'.") |
| lengthKind match { |
| // case LengthKind.Delimited => LiteralNilDelimitedOrEndOfData(this) |
| case LengthKind.Delimited if this.hasExpressionsInTerminatingMarkup => LiteralNilDelimitedEndOfDataDynamic(this) |
| case LengthKind.Delimited => LiteralNilDelimitedEndOfDataStatic(this) |
| case LengthKind.Pattern => LiteralNilPattern(this) |
| case LengthKind.Explicit => { |
| lengthUnits match { |
| case LengthUnits.Bits => notYetImplemented("nilKind='literalValue' with lengthKind='bits'") |
| case LengthUnits.Bytes => LiteralNilExplicitLengthInBytes(this) |
| case LengthUnits.Characters => LiteralNilExplicitLengthInChars(this) |
| } |
| } |
| case LengthKind.Implicit => { |
| schemaDefinition(representation != Representation.Text, "LiteralValue Nils with lengthKind='implicit' cannot have representation='text'.") |
| val lengthInBytes = implicitBinaryLengthInBits / 8 |
| LiteralNilKnownLengthInBytes(this, lengthInBytes) |
| } |
| case LengthKind.Prefixed => notYetImplemented("lengthKind='prefixed'") |
| case LengthKind.EndOfParent => notYetImplemented("lengthKind='endOfParent'") |
| } |
| } ~ nilElementTerminator) |
| } |
| |
| lazy val scalarDefaultableSimpleContent = { |
| val res = Prod("scalarDefaultableSimpleContent", this, |
| isSimpleType, nilLit | emptyDefaulted | parsedNil | parsedValue) |
| res |
| } |
| |
| lazy val scalarNonDefaultSimpleContent = { |
| val res = Prod("scalarNonDefaultSimpleContent", this, |
| isSimpleType, nilLit | parsedNil | parsedValue) |
| res |
| } |
| |
| def specifiedLength(body: => Gram) = { |
| lengthKind match { |
| case LengthKind.Pattern => new SpecifiedLengthPattern(this, body) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Bits && isFixedLength => new SpecifiedLengthExplicitBitsFixed(this, body, fixedLength) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Bits && !isFixedLength => new SpecifiedLengthExplicitBits(this, body) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Bytes && isFixedLength => new SpecifiedLengthExplicitBytesFixed(this, body, fixedLength) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Bytes && !isFixedLength => new SpecifiedLengthExplicitBytes(this, body) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Characters && isFixedLength => new SpecifiedLengthExplicitCharactersFixed(this, body, fixedLength) |
| case LengthKind.Explicit if lengthUnits == LengthUnits.Characters && !isFixedLength => new SpecifiedLengthExplicitCharacters(this, body) |
| case _ => { |
| // TODO: implement other specified length restrictions |
| // for now, no restriction |
| body |
| } |
| } |
| |
| } |
| |
| lazy val scalarComplexContent = Prod("scalarComplexContent", this, isComplexType, specifiedLength(nilLit | complexContent)) |
| |
| // Note: there is no such thing as defaultable complex content because you can't have a |
| // default value for a complex type element. |
| lazy val scalarDefaultableContent = Prod("scalarDefaultableContent", this, scalarDefaultableSimpleContent | scalarComplexContent) |
| |
| lazy val scalarNonDefaultContent = Prod("scalarNonDefaultContent", this, scalarNonDefaultSimpleContent | scalarComplexContent) |
| |
| /** |
| * the element left framing does not include the initiator nor the element right framing the terminator |
| */ |
| lazy val elementLeftFraming = Prod("elementLeftFraming", this, |
| leadingSkipRegion ~ alignmentFill ~ PrefixLength(this)) |
| |
| lazy val elementRightFraming = Prod("elementRightFraming", this, trailingSkipRegion) |
| |
| lazy val dfdlElementBegin = Prod("dfdlElementBegin", this, ElementBegin(this)) |
| |
| lazy val dfdlElementEnd = Prod("dfdlElementEnd", this, { |
| if (isRepresented) ElementEnd(this) |
| else ElementEndNoRep(this) |
| }) |
| |
| // lazy val scalarNonDefault = Prod("scalarNonDefault", this, |
| // dfdlElementBegin ~ elementLeftFraming ~ dfdlScopeBegin ~ |
| // scalarNonDefaultContent ~ elementRightFraming ~ dfdlStatementEvaluations ~ dfdlScopeEnd ~ dfdlElementEnd) |
| |
| lazy val scalarNonDefaultPhysical = Prod("scalarNonDefault", this, |
| StmtEval(this, dfdlElementBegin ~ elementLeftFraming ~ dfdlScopeBegin ~ |
| scalarNonDefaultContent) ~ elementRightFraming ~ dfdlScopeEnd ~ dfdlElementEnd) |
| |
| // def scalarDefaultable: Prod |
| // |
| // def scalarNonDefault: Prod |
| lazy val scalarDefaultable = Prod("scalarDefaultable", this, |
| if (inputValueCalcOption == None) { |
| scalarDefaultablePhysical |
| } else { |
| inputValueCalcElement |
| }) |
| |
| lazy val scalarNonDefault = Prod("scalarNonDefault", this, |
| if (inputValueCalcOption == None) { |
| scalarNonDefaultPhysical |
| } else { |
| inputValueCalcElement |
| }) |
| |
| lazy val inputValueCalcElement = Prod("inputValueCalcElement", this, |
| isSimpleType && inputValueCalcOption != None, |
| StmtEval(this, dfdlElementBegin ~ dfdlScopeBegin ~ |
| InputValueCalc(self)) ~ dfdlScopeEnd ~ dfdlElementEnd) |
| |
| lazy val scalarDefaultablePhysical = Prod("scalarDefaultablePhysical", this, { |
| val res = StmtEval(this, dfdlElementBegin ~ elementLeftFraming ~ dfdlScopeBegin ~ |
| scalarDefaultableContent) ~ elementRightFraming ~ dfdlScopeEnd ~ dfdlElementEnd |
| res |
| }) |
| |
| } |
| |
| trait ElementReferenceGrammarMixin { self: ElementRef => |
| override lazy val termContentBody = self.referencedElement.termContentBody |
| } |
| |
| trait LocalElementGrammarMixin { self: LocalElementBase => |
| |
| lazy val termContentBody = { |
| val res = Prod("termContentBody", self, separatedScalarDefaultable | recurrance) |
| res |
| } |
| |
| lazy val allowedValue = Prod("allowedValue", this, notStopValue | value) |
| |
| lazy val notStopValue = Prod("notStopValue", this, hasStopValue, NotStopValue(this)) |
| |
| lazy val separatedEmpty = Prod("separatedEmpty", this, emptyIsAnObservableConcept, separatedForPosition(empty)) |
| lazy val separatedScalarDefaultable = Prod("separatedScalarDefaultable", this, isScalar, separatedForPosition(scalarDefaultable)) |
| lazy val separatedRecurringDefaultable = Prod("separatedRecurringDefaultable", this, !isScalar, separatedForPosition(scalarDefaultable)) |
| lazy val separatedScalarNonDefault = Prod("separatedScalarNonDefault", this, isScalar, separatedForPosition(scalarNonDefault)) |
| lazy val separatedRecurringNonDefault = Prod("separatedRecurringNonDefault", this, !isScalar, separatedForPosition(scalarNonDefault)) |
| |
| lazy val nonSeparatedScalarDefaultable = Prod("nonSeparatedScalarDefaultable", this, isScalar, scalarDefaultable) |
| |
| lazy val recurrance = Prod("recurrance", this, |
| !isScalar, |
| StartArray(this) ~ arrayContents ~ EndArray(this) ~ FinalUnusedRegion(this)) |
| |
| override lazy val asTermInChoice = { |
| val res = Prod("asTermInChoice", this, nonSeparatedScalarDefaultable | recurrance) |
| res |
| } |
| |
| /** |
| * speculate parsing forward until we get an error |
| */ |
| lazy val separatedContentUnboundedWithoutTrailingEmpties = Prod("separatedContentUnboundedWithoutTrailingEmpties", this, isRecurring, |
| RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~ |
| RepUnbounded(self, separatedRecurringNonDefault) ~ |
| StopValue(this)) |
| |
| lazy val separatedContentUnbounded = Prod("separatedContentUnbounded", this, isRecurring, |
| separatedContentUnboundedWithoutTrailingEmpties // These are for tolerating trailing empties. Let's not tolerate them for now. |
| // ~ |
| // RepUnbounded(separatedEmpty) |
| ) |
| |
| lazy val separatedContentAtMostNWithoutTrailingEmpties = Prod("separatedContentAtMostNWithoutTrailingEmpties", this, isRecurring, |
| RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~ |
| RepAtMostTotalN(this, maxOccurs, separatedRecurringNonDefault) ~ |
| StopValue(this)) |
| |
| // TODO: Do we have to adjust the count to take stopValue into account? |
| // Answer: No because the counts are never used when there is a stopValue (at least in current |
| // thinking about how occursCountKind='stopValue' works.) |
| |
| lazy val separatedContentAtMostN = Prod("separatedContentAtMostN", this, isRecurring, |
| separatedContentAtMostNWithoutTrailingEmpties ~ |
| RepAtMostTotalN(self, maxOccurs, separatedEmpty)) // absorb extra separators, if found. |
| |
| /** |
| * parse counted number of occurrences exactly. |
| */ |
| lazy val stopValueSize = if (hasStopValue) 1 else 0 |
| |
| // TODO FIXME: We really want to have different productions for parsing and unparsing in these |
| // complex cases where there is defaulting, etc. Unparsing has many fewer cases, and is just not |
| // symmetric with parsing in these situations. |
| def separatedContentExactlyN(count: Long) = { |
| RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~ |
| RepAtMostTotalN(self, count, separatedRecurringNonDefault) ~ |
| StopValue(this) ~ |
| RepExactlyTotalN(self, maxOccurs + stopValueSize, separatedEmpty) // absorb reps remaining separators |
| } |
| |
| lazy val separatedContentExactlyNComputed = { |
| OccursCountExpression(this) ~ |
| RepAtMostOccursCount(this, minOccurs, separatedRecurringDefaultable) ~ |
| RepExactlyTotalOccursCount(this, separatedRecurringNonDefault) |
| } |
| |
| // keep in mind that anything here that scans for a representation either knows the length it is going after, or knows what the terminating markup is, and |
| // our invariant is, that it does NOT consume that markup ever. The parser consumes it with appropriate grammar terminals. |
| |
| val UNB = -1 // UNBOUNDED |
| |
| lazy val arrayContents = { |
| val res = Prod("arrayContents", this, isRecurring, |
| arrayContentsNoSeparators | arrayContentsWithSeparators) |
| res |
| } |
| |
| lazy val contentUnbounded = { |
| |
| val res = Prod("contentUnbounded", this, isRecurring, RepUnbounded(self, separatedRecurringDefaultable)) |
| res |
| } |
| |
| lazy val arrayContentsNoSeparators = Prod("arrayContentsNoSeparators", this, isRecurring && !hasSep, { |
| val max = maxOccurs |
| val min = minOccurs |
| val res = occursCountKind match { |
| case Expression => separatedContentExactlyNComputed |
| case OccursCountKind.Fixed if (max == UNB) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs") |
| case OccursCountKind.Fixed if (min != max) => SDE("occursCountKind='fixed' requires minOccurs and maxOccurs to be equal (%d != %d)", min, max) |
| case OccursCountKind.Fixed => separatedContentExactlyN(max) |
| case OccursCountKind.Implicit if (max == UNB) => contentUnbounded // same as parsed |
| case OccursCountKind.Implicit => separatedContentAtMostN // uses maxOccurs |
| case OccursCountKind.Parsed => contentUnbounded |
| case OccursCountKind.StopValue => contentUnbounded |
| } |
| res |
| }) |
| |
| // |
| // Silly constants to make the lookup table below more readable without using fragile whitespace |
| val Never______ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Never |
| val TrailingLax: SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingLax |
| val Trailing___ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Trailing |
| val Always_____ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Always |
| val StopValue_ = OccursCountKind.StopValue |
| val Implicit__ = OccursCountKind.Implicit |
| val Parsed____ = OccursCountKind.Parsed |
| val Fixed_____ = OccursCountKind.Fixed |
| val Expression = OccursCountKind.Expression |
| /** |
| * Matches the table about separator suppression policy. |
| * |
| * TODO: Right now that table is in DFDL WG subgroup working on "Issue 140" which is trying to |
| * rationalize separator suppression among other things. Update this table to match the final spec. |
| */ |
| lazy val arrayContentsWithSeparators = Prod("arrayContentsWithSeparators", this, isRecurring && hasSep, { |
| val triple = (separatorSuppressionPolicy, occursCountKind, maxOccurs) |
| val res = triple match { |
| case (___________, Expression, ___) => separatedContentExactlyNComputed |
| case (___________, Fixed_____, UNB) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs") |
| case (___________, Fixed_____, max) if (max != minOccurs) => SDE("occursCountKind='fixed' requires minOccurs to equal maxOccurs (%d != %d)", minOccurs, max) |
| case (___________, Fixed_____, max) => separatedContentExactlyN(max) |
| case (Never______, Implicit__, UNB) => SDE("separatorSuppressionPolicy='never' with occursCountKind='implicit' required bounded maxOccurs.") |
| case (Never______, Implicit__, max) => separatedContentExactlyN(max) |
| case (Never______, ock, ___) => SDE("separatorSuppressionPolicy='never' not allowed in combination with occursCountKind='" + ock + "'.") |
| case (TrailingLax, Implicit__, UNB) if (!isLastRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence") |
| case (TrailingLax, Implicit__, UNB) => separatedContentUnbounded |
| case (TrailingLax, Implicit__, max) => separatedContentAtMostN // FIXME: have to have all of them - not trailing position |
| case (Trailing___, Implicit__, UNB) if (!isLastRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence") |
| case (Trailing___, Implicit__, UNB) => separatedContentUnboundedWithoutTrailingEmpties // we're depending on optionalEmptyPart failing on empty content. |
| case (Trailing___, Implicit__, max) => separatedContentAtMostNWithoutTrailingEmpties |
| case (Always_____, Implicit__, UNB) => separatedContentUnbounded |
| case (Always_____, Implicit__, max) => separatedContentAtMostN |
| case (Always_____, Parsed____, ___) => separatedContentUnbounded |
| case (Always_____, StopValue_, ___) => separatedContentUnbounded |
| case (policy, ock, max) => SDE("separatorSuppressionPolicy='" + policy + "' not allowed with occursCountKind='" + ock + "'.") |
| } |
| res |
| }) |
| } |
| |
| trait ElementDeclGrammarMixin { self: ElementBase with ElementDeclMixin => |
| |
| override lazy val inputValueCalcOption = getPropertyOption("inputValueCalc") |
| |
| } |
| |
| trait GlobalElementDeclGrammarMixin |
| extends LocalElementGrammarMixin // can be repeating if not root |
| { self: GlobalElementDecl => |
| |
| lazy val documentElement = Prod("documentElement", this, scalarDefaultable) |
| |
| lazy val document = Prod("document", this, { |
| UnicodeByteOrderMark(this) ~ documentElement |
| }) |
| |
| } |
| |
| ///////////////////////////////////////////////////////////////// |
| // Groups System |
| ///////////////////////////////////////////////////////////////// |
| |
| trait TermGrammarMixin { self: Term => |
| |
| lazy val newVars = this.annotationObjs.filter { st => |
| st.isInstanceOf[DFDLNewVariableInstance] |
| }.asInstanceOf[Seq[DFDLNewVariableInstance]] |
| |
| lazy val newVarStarts = newVars.map { _.gram } |
| lazy val newVarEnds = newVars.map { _.endGram } |
| |
| lazy val dfdlScopeBegin = Prod("dfdlScopeBegin", this, newVarStarts.length > 0, |
| newVarStarts.fold(EmptyGram) { _ ~ _ }) |
| |
| lazy val dfdlScopeEnd = Prod("dfdlScopeEnd", this, newVarEnds.length > 0, |
| newVarEnds.fold(EmptyGram) { _ ~ _ }) |
| |
| def termContentBody: Prod |
| |
| // I am not sure we need to distinguish these two. |
| lazy val asTermInSequence = termContentBody |
| lazy val asTermInChoice = termContentBody |
| |
| def separatedForPosition(body: => Gram) = { |
| if (!isRepresented) body // no separators for things that have no representation in the data stream |
| else { |
| val res = prefixSep ~ infixSepRule ~ body ~ postfixSep |
| res |
| } |
| } |
| |
| lazy val Some(es) = { |
| // |
| // Not sure how to assert this, |
| // but an invariant we're assuming here is that we are NOT the |
| // root element, which has no enclosing sequence at all. |
| // |
| // The grammar rules shouldn't be asking for separated stuff |
| // in that situation, so we shouldn't be here. |
| // |
| // TODO: FIXME: |
| // Also note: we can get away with just looking upward for nearest enclosing |
| // sequence because we have restrictions on what can be inside a choice, |
| // and we disallow delimiters on choices. If one allows delimiters on |
| // choices... consider |
| // <sequence dfdl:separator=","> |
| // <choice dfdl:initiator="[", terminator="]"> |
| // <element ref="foo" maxOccurs="20"/> |
| // ... |
| // In this case, what separates the multiple occurrances of foo? I claim |
| // they are comma separated. |
| // But data could be like this 'a, b, c,[foo1,foo2,foo3],d,e,f' |
| // |
| // Not unreasonable, but just too much complexity. Postpone until later. |
| |
| // |
| // TODO: fix this when those restrictions are lifted. |
| // |
| subset(hasES, "(Current restriction) There must be an enclosing sequence.") |
| nearestEnclosingSequence |
| } |
| |
| def hasES = nearestEnclosingSequence != None |
| def ignoreES = inChoiceBeforeNearestEnclosingSequence == true |
| |
| lazy val staticSeparator = Prod("staticSeparator", this, !ignoreES && hasES && es.separator.isConstant, |
| StaticSeparator(es, self)) |
| |
| lazy val dynamicSeparator = Prod("dynamicSeparator", this, !ignoreES && hasES && !es.separator.isConstant, |
| DynamicSeparator(es, self)) |
| |
| lazy val sepRule = staticSeparator | dynamicSeparator |
| |
| lazy val prefixSep = Prod("prefixSep", this, |
| { |
| val res = !ignoreES && hasES && es.hasPrefixSep |
| res |
| }, |
| sepRule) |
| |
| lazy val postfixSep = Prod("postfixSep", this, !ignoreES && hasES && es.hasPostfixSep, sepRule) |
| lazy val infixSep = Prod("infixSep", this, !ignoreES && hasES && es.hasInfixSep, sepRule) |
| |
| lazy val isStaticallyFirst = { |
| es.hasInfixSep && |
| this.positionInNearestEnclosingSequence == 1 && |
| isScalar && |
| !hasPriorRequiredSiblings |
| } |
| |
| lazy val infixSepRule = Prod("infixSepRule", this, |
| !ignoreES && hasES && es.hasInfixSep, { |
| if (isStaticallyFirst) Nada(this) // we're first, no infix sep. |
| else if (hasPriorRequiredSiblings) infixSep // always in this case |
| else if (positionInNearestEnclosingSequence > 1 || !isScalar) { |
| // runtime check for group pos such that we need a separator. |
| // Note that GroupPosGreaterThan(N,..) sets discriminator, so if it is true, and infixSep is not found, it won't |
| // backtrack and try nothing. Only if GroupPos is not greater than N will it backtrack. |
| // TODO: adding ChildPosGreaterThan and ArrayPosGreaterThan fixes bug with xs:choice and array tests--check for other cases |
| (ArrayPosGreaterThan(1, self) ~ infixSep) | |
| ((GroupPosGreaterThan(1, self) ~ infixSep) | Nada(this)) |
| } else Assert.invariantFailed("infixSepRule didn't understand what to lay down as grammar for this situation: " + this) |
| }) |
| |
| } |
| |
| trait HasStatementsGrammarMixin { self: Term with DFDLStatementMixin => |
| |
| final lazy val statementGrams = statements.map { _.gram } |
| // TODO: statements (but specifically not newVariableInstance) can appear on simple type definitions as well as terms. |
| |
| lazy val dfdlStatementEvaluations = Prod("dfdlStatementEvaluations", this, statementGrams.length > 0, |
| statementGrams.fold(EmptyGram) { _ ~ _ }) |
| } |
| |
| trait ModelGroupGrammarMixin |
| extends InitiatedTerminatedMixin |
| with AlignedMixin |
| with HasStatementsGrammarMixin |
| with GroupCommonAGMixin { self: ModelGroup => |
| |
| lazy val groupLeftFraming = Prod("groupLeftFraming", this, leadingSkipRegion ~ alignmentFill ~ initiatorRegion) |
| lazy val groupRightFraming = Prod("groupRightFraming", this, terminatorRegion ~ trailingSkipRegion) |
| |
| // I believe we can have the same grammar rules whether we're directly inside a complex type, or |
| // we're nested inside another group as a term. |
| lazy val asChildOfComplexType = termContentBody |
| |
| lazy val modelGroupSyntax = Prod("modelGroupSyntax", this, dfdlStatementEvaluations ~ groupLeftFraming ~ groupContent ~ groupRightFraming) |
| |
| lazy val termContentBody = Prod("termContentBody", this, separatedForPosition(modelGroupSyntax)) |
| |
| def mt = EmptyGram.asInstanceOf[Gram] // cast trick to shut up foldLeft compile errors below |
| |
| def groupContent: Prod |
| } |
| |
| trait ChoiceGrammarMixin { self: Choice => |
| |
| lazy val groupContent = Prod("choiceContent", this, alternatives.foldRight(mt)(folder)) |
| |
| def folder(p: Gram, q: Gram): Gram = p | q |
| |
| lazy val alternatives = groupMembers.map { _.asTermInChoice } |
| } |
| |
| trait SequenceGrammarMixin { self: Sequence => |
| |
| lazy val groupContent = Prod("sequenceContent", this, StartSequence(this) ~ terms.foldRight(mt)(folder) ~ EndSequence(this)) |
| |
| def folder(p: Gram, q: Gram): Gram = p ~ q |
| |
| lazy val terms = groupMembers.map { _.asTermInSequence } |
| |
| /** |
| * These are static properties even though the delimiters can have runtime-computed values. |
| * The existence of an expression to compute a delimiter is assumed to imply a non-zero-length, aka a real delimiter. |
| */ |
| lazy val hasPrefixSep = sepExpr(SeparatorPosition.Prefix) |
| |
| lazy val hasInfixSep = sepExpr(SeparatorPosition.Infix) |
| |
| lazy val hasPostfixSep = sepExpr(SeparatorPosition.Postfix) |
| |
| lazy val hasSeparator = separator.isKnownNonEmpty |
| |
| // note use of pass by value. We don't want to even need the SeparatorPosition property unless there is a separator. |
| def sepExpr(pos: => SeparatorPosition): Boolean = { |
| if (hasSeparator) if (separatorPosition eq pos) true else false |
| else false |
| } |
| } |
| |
| trait GroupRefGrammarMixin { self: GroupRef => |
| |
| def termContentBody = self.group.termContentBody |
| |
| } |
| |
| ///////////////////////////////////////////////////////////////// |
| // Types System |
| ///////////////////////////////////////////////////////////////// |
| |
| trait ComplexTypeBaseGrammarMixin { self: ComplexTypeBase => |
| lazy val startChildren = StartChildren(this, true) |
| lazy val endChildren = EndChildren(this, true) |
| |
| lazy val mainGrammar = Prod("mainGrammar", self.element, startChildren ~ modelGroup.group.asChildOfComplexType ~ endChildren) |
| |
| } |