blob: eeb7bc22ed4d2a9941a34c1ab345bd7dbaa1025a [file] [log] [blame]
/*
* 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.grammar
import org.apache.daffodil.exceptions.Assert
import org.apache.daffodil.Implicits._; object INoWarn { ImplicitsSuppressUnusedImportWarning() }
import org.apache.daffodil.processors.unparsers.SeqCompUnparser
import org.apache.daffodil.dsom._
import org.apache.daffodil.grammar.primitives.Nada
import org.apache.daffodil.processors.parsers.SeqCompParser
import org.apache.daffodil.processors.parsers.AltCompParser
import org.apache.daffodil.compiler.ForUnparser
import org.apache.daffodil.compiler.ForParser
import org.apache.daffodil.processors.unparsers.NadaUnparser
import org.apache.daffodil.processors.parsers.NadaParser
import org.apache.daffodil.processors.unparsers.EmptyGramUnparser
abstract class UnaryGram(context: Term, rr: => Gram) extends NamedGram(context) {
private lazy val r = rr
final override lazy val gram = {
if (r.isEmpty) EmptyGram
else this
}
}
/**
* BinaryGram isn't really 'binary' it's n-ary. It is called binary because it comes from
* the binary grammar operations ~ and |, but in the abstract syntax tree we want
* these flattened to lists of children so that a ~ b ~ c is ONE SeqComp with 3 children, not a tree
* of two binary SeqComps.
*/
abstract class BinaryGram(context: SchemaComponent, childrenArg: Seq[Gram]) extends Gram(context) {
requiredEvaluations(children)
protected def op: String
protected def open: String
protected def close: String
protected final lazy val children = {
val c = childrenArg
c
}
override def toString = {
Assert.invariant(children != null)
Assert.invariant(open != null)
Assert.invariant(op != null)
Assert.invariant(close != null)
def toStringpq(p: String, q: String) =
open + p + " " + op + " " + q + close
val res = children.map { _.toString }.reduce { toStringpq _ }
res
}
}
/**
* Sequential composition of grammar terms.
*
* Flattens nests of these into a flat list of terms.
*/
object SeqComp {
def apply(context: SchemaComponent, p: => Gram, q: => Gram) = {
val children = (p, q) match {
case (ps: SeqComp, qs: SeqComp) => {
ps.children ++ qs.children
}
case (ps: SeqComp, _) => ps.children ++ List(q)
case (_, qs: SeqComp) => p +: qs.children
case (_, _) => List(p, q)
}
val res = new SeqComp(context, children)
res
}
}
class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends BinaryGram(context, children) {
protected final override def op = "~"
protected final override def open = "("
protected final override def close = ")"
Assert.invariant(!children.exists { _.isInstanceOf[Nada] })
lazy val parserChildren = children.filter(_.forWhat != ForUnparser).map { _.parser }.filterNot { _.isInstanceOf[NadaParser] }
final override lazy val parser = {
if (parserChildren.isEmpty) new NadaParser(context.runtimeData)
else if (parserChildren.length == 1) parserChildren(0)
else new SeqCompParser(context.runtimeData, parserChildren.toArray)
}
lazy val unparserChildren = {
val unparserKeptChildren =
children.filter(
x =>
!x.isEmpty && (x.forWhat != ForParser))
val unparsers =
unparserKeptChildren.map { x =>
x.unparser
}.filterNot { _.isInstanceOf[NadaUnparser] }
unparsers
}
final override lazy val unparser = {
if (unparserChildren.isEmpty) new NadaUnparser(context.runtimeData)
else if (unparserChildren.length == 1) unparserChildren(0)
else new SeqCompUnparser(context.runtimeData, unparserChildren.toArray)
}
}
/**
* Alternative composition of grammar terms.
*
* Flattens nests of these into a single flat list.
*/
object AltComp {
def apply(context: SchemaComponent, p: => Gram, q: => Gram) = {
val children = (p, q) match {
case (ps: AltComp, qs: AltComp) => {
ps.children ++ qs.children
}
case (ps: AltComp, _) => ps.children ++ List(q)
case (_, qs: AltComp) => p +: qs.children
case (_, _) => List(p, q)
}
val res = new AltComp(context, children)
res
}
}
class AltComp private (context: SchemaComponent, children: Seq[Gram]) extends BinaryGram(context, children)
with HasNoUnparser {
protected final override def op = "|"
protected final override def open = "["
protected final override def close = "]"
final override lazy val parser = new AltCompParser(context.runtimeData, children.map { _.parser })
}
object EmptyGram extends Gram(null) {
override def isEmpty = true
override def toString = "Empty"
override lazy val parser = new NadaParser(null)
override lazy val unparser = // hasNoUnparser
// we depend on this unparser being returned, even though it cannot be called to unparse anything.
// As there are unit tests which test attributes where those attributes cause a DummyUnparser to be created.
// That is later optimized out (or needs to be).
//
// FIXME: switch back to hasNoUnparser, find where this is being used and have it not ask for
// this unparser.
new EmptyGramUnparser(null) // DummyUnparser(Misc.getNameFromClass(this))
}
object ErrorGram extends Gram(null) with HasNoUnparser {
override def isEmpty = false
override def toString = "Error"
override lazy val parser = hasNoParser // new ErrorParser
}
abstract class NamedGram(context: SchemaComponent) extends Gram(context) {
// Note: keep the toString really simple.
// It causes much grief if toString uses complicated things that can fail or
// that end up needing the name of this NamedGram again.
override def name = context match {
case nm: NamedMixin => nm.name
case _ => super.name
}
override def toString = "<" + name + ">" + super.name + "</" + name + ">"
}
/**
* Primitives will derive from this base
*/
abstract class Terminal(contextArg: SchemaComponent, guard: Boolean)
extends NamedGram(contextArg) {
override def isEmpty = !guard
}