You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2021/01/27 15:20:29 UTC

[incubator-daffodil] branch daffodil-758-debug-delimiter created (now a73bf72)

This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a change to branch daffodil-758-debug-delimiter
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git.


      at a73bf72  Update debugger support of found delimiters/fields/diffs

This branch includes the following new commits:

     new a73bf72  Update debugger support of found delimiters/fields/diffs

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-daffodil] 01/01: Update debugger support of found delimiters/fields/diffs

Posted by sl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch daffodil-758-debug-delimiter
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git

commit a73bf7247da0a49a9d9cb45ff72dfbd7c80a9d9a
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Tue Jan 26 14:45:55 2021 -0500

    Update debugger support of found delimiters/fields/diffs
    
    - Refactor how diff's are calculated so that all the logic is moved into
      the individual command objects that care about that piece of
      information. Now, any info command that is "diffable" must implement
      the InfoDiffable trait and the 'diff' function. The "info diff"
      command now automatically finds all commands that implement this trait
      and will call this diff function. This simplifies the info diff
      command and makes it so it doesn't need to know anything about other
      info commands or anything about the state.
    - Instead of "info foundDelimiter" showing both the found delimiter and
      the found field, it now only shows the found delimiter. And add a new
      "info foundField" command to show the found field that "info
      foundDelimiter" used to show. This makes the code less complex and
      gives the user more control over what information they want to see.
      These new commands are also made diffable so they now show up in "info
      diff". When diffs are found, this command outputs something like this:
    
        diff:
          foundDelimiter: none -> ,
          foundField: none -> smith
    
      The above shows that a comma delimiter was found and that the field
      proceeding that delimiter was "smith".
    
    DAFFODIL-758
---
 .../daffodil/debugger/InteractiveDebugger.scala    | 282 ++++++++++++++-------
 .../daffodil/processors/ProcessorStateBases.scala  |  24 +-
 .../daffodil/processors/unparsers/UState.scala     |   2 +
 3 files changed, 212 insertions(+), 96 deletions(-)

diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
index 35f8e31..22a3d78 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
@@ -17,39 +17,41 @@
 
 package org.apache.daffodil.debugger
 
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.schema.annotation.props.gen.Representation
-import org.apache.daffodil.processors._
-import org.apache.daffodil.infoset._
-import org.apache.daffodil.processors.parsers._
-import org.apache.daffodil.xml.XMLUtils
-import org.apache.daffodil.xml.GlobalQName
-import org.apache.daffodil.xml.QName
 import java.io.File
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable
+
+import jline.console.completer.AggregateCompleter
 import jline.console.completer.Completer
 import jline.console.completer.StringsCompleter
-import jline.console.completer.AggregateCompleter
-import org.apache.daffodil.util.Enum
-import org.apache.daffodil.dsom.ExpressionCompilerClass
-import org.apache.daffodil.dpath.NodeInfo
+
+import org.apache.daffodil.BasicComponent
+import org.apache.daffodil.api.DaffodilTunables
 import org.apache.daffodil.dpath.ExpressionEvaluationException
-import org.apache.daffodil.util.DPathUtil
-import org.apache.daffodil.util.Misc
-import org.apache.daffodil.oolag.ErrorsNotYetRecorded
-import org.apache.daffodil.processors.unparsers.UState
-import org.apache.daffodil.processors.unparsers.Unparser
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.dsom.ExpressionCompilerClass
 import org.apache.daffodil.dsom.RelativePathPastRootError
-import org.apache.daffodil.exceptions.UnsuppressableException
-import scala.collection.mutable
 import org.apache.daffodil.dsom.RuntimeSchemaDefinitionError
-import org.apache.daffodil.util.Misc
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.UnsuppressableException
 import org.apache.daffodil.infoset.InfosetElement
 import org.apache.daffodil.infoset.XMLTextInfosetOutputter
-import org.apache.daffodil.processors.parsers.ConvertTextCombinatorParser
+import org.apache.daffodil.infoset._
+import org.apache.daffodil.oolag.ErrorsNotYetRecorded
 import org.apache.daffodil.oolag.OOLAG._
-import scala.collection.JavaConverters._
-import org.apache.daffodil.api.DaffodilTunables
-import org.apache.daffodil.BasicComponent
+import org.apache.daffodil.processors._
+import org.apache.daffodil.processors.parsers.ConvertTextCombinatorParser
+import org.apache.daffodil.processors.parsers._
+import org.apache.daffodil.processors.unparsers.UState
+import org.apache.daffodil.processors.unparsers.Unparser
+import org.apache.daffodil.schema.annotation.props.gen.Representation
+import org.apache.daffodil.util.DPathUtil
+import org.apache.daffodil.util.Enum
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.xml.GlobalQName
+import org.apache.daffodil.xml.QName
+import org.apache.daffodil.xml.XMLUtils
 
 abstract class InteractiveDebuggerRunner {
   def init(id: InteractiveDebugger): Unit
@@ -465,7 +467,7 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
   }
 
 /**********************************/
-  /**          Commands            **/
+/**          Commands            **/
 /**********************************/
 
   abstract class DebugCommand {
@@ -1205,6 +1207,7 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
           InfoDiff,
           InfoDisplays,
           InfoFoundDelimiter,
+          InfoFoundField,
           InfoGroupIndex,
           InfoHidden,
           InfoInfoset,
@@ -1307,11 +1310,25 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         }
       }
 
-      object InfoBitLimit extends DebugCommand with DebugCommandValidateZeroArgs {
+      trait InfoDiffable {
+        /**
+        * Outputs any differences between prestate and state for the mixed in debugger command
+        *
+        * Differences should be displayed via the debugPrintln command. Output
+        * should include the command name and two space indentation (e.g. pass
+        * in "  " as the second argument of debugPrintln).
+        *
+        * @return true if any differences were found and output, false otherwise
+        */
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean
+      }
+
+      object InfoBitLimit extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "bitLimit"
         override lazy val short = "bl"
         val desc = "display the current bit limit"
         val longDesc = desc
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
           if (state.bitLimit0b.isDefined) {
             debugPrintln("%s: %d".format(name, state.bitLimit0b.get))
@@ -1320,21 +1337,40 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
           }
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          if (prestate.bitLimit0b != state.bitLimit0b) {
+            debugPrintln("%s: %d -> %d".format(name, prestate.bitLimit0b, state.bitLimit0b), "  ")
+            true
+          } else {
+            false
+          }
+        }
       }
 
-      object InfoBitPosition extends DebugCommand with DebugCommandValidateZeroArgs {
+      object InfoBitPosition extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "bitPosition"
         override lazy val short = "bp"
         val desc = "display the current bit position"
         val longDesc = desc
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
-          if (state.bitPos != -1) {
-            debugPrintln("%s: %d".format(name, state.bitPos))
+          if (state.bitPos0b != -1) {
+            debugPrintln("%s: %d".format(name, state.bitPos0b))
           } else {
             debugPrintln("%s: no bit position set".format(name))
           }
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state:ParseOrUnparseState): Boolean = {
+          if (prestate.bitPos0b != state.bitPos0b) {
+            debugPrintln("%s: %d -> %d".format(name, prestate.bitPos0b, state.bitPos0b), "  ");
+            true
+          } else {
+            false
+          }
+        }
       }
 
       object InfoBreakpoints extends DebugCommand with DebugCommandValidateZeroArgs {
@@ -1357,11 +1393,12 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         }
       }
 
-      object InfoChildIndex extends DebugCommand with DebugCommandValidateZeroArgs {
+      object InfoChildIndex extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "childIndex"
         override lazy val short = "ci"
         val desc = "display the current child index"
         val longDesc = desc
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
           if (state.childPos != -1) {
             debugPrintln("%s: %d".format(name, state.childPos))
@@ -1370,6 +1407,15 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
           }
           DebugState.Pause
         }
+ 
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          if (prestate.childPos != state.childPos) {
+            debugPrintln("%s: %d -> %d".format(name, prestate.childPos, state.childPos), "  ")
+            true
+          } else {
+            false
+          }
+        }
       }
 
       object InfoData extends DebugCommand with DebugCommandValidateOptionalArg {
@@ -1459,47 +1505,15 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         override lazy val short = "diff"
         val desc = "display the differences from the previous state"
         val longDesc = desc
+
+        private lazy val infoDiffables = Info.subcommands.collect { case diffable: InfoDiffable => diffable }
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
           debugPrintln("%s:".format(name))
-          var diff = false
-          (prestate, state) match {
-            case (prestate: StateForDebugger, state: ParseOrUnparseState) => {
-              if (prestate.bytePos != state.bytePos) { debugPrintln("position (bytes): %d -> %d".format(prestate.bytePos, state.bytePos), "  "); diff = true }
-              if (prestate.bitLimit0b != state.bitLimit0b) { debugPrintln("bitLimit: %d -> %d".format(prestate.bitLimit0b, state.bitLimit0b), "  "); diff = true }
-              if (prestate.arrayPos != state.arrayPos) { debugPrintln("occursIndex: %d -> %d".format(prestate.arrayPos, state.arrayPos), "  "); diff = true }
-              if (prestate.groupPos != state.groupPos) { debugPrintln("groupIndex: %d -> %d".format(prestate.groupPos, state.groupPos), "  "); diff = true }
-              if (prestate.childPos != state.childPos) { debugPrintln("childIndex: %d -> %d".format(prestate.childPos, state.childPos), "  "); diff = true }
-              prestate.variableMap.qnames.foreach { qname =>
-                val pre_instance = prestate.variableMap.find(qname).get
-                val pre_value = pre_instance.value
-                val pre_state = pre_instance.state
-
-                val cur_instance = state.variableMap.find(qname).get
-                val cur_value = cur_instance.value
-                val cur_state = cur_instance.state
-
-                if (pre_value != cur_value || pre_state != cur_state) {
-                  debugPrintln("variable: %s: %s -> %s".format(
-                    qname,
-                    InfoVariables.variableInstanceToDebugString(pre_instance),
-                    InfoVariables.variableInstanceToDebugString(cur_instance),
-                  ), "  ")
-                  diff = true
-                }
-              }
-            }
-            case _ => // ok
+          val differencesExist = infoDiffables.foldLeft(false) { case (foundDiff, ic) =>
+            ic.diff(prestate, state) || foundDiff
           }
-          (prestate, state) match {
-            case (prestate: StateForDebugger, state: PState) => {
-              // nothing yet that is specific to Parser
-            }
-            case (prestate: StateForDebugger, state: UState) => {
-              // nothing yet that is specific to Unparser
-            }
-            case _ => Assert.impossibleCase()
-          }
-          if (diff == false) {
+          if (!differencesExist) {
             debugPrintln("No differences", "  ")
           }
 
@@ -1528,37 +1542,91 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         }
       }
 
-      object InfoFoundDelimiter extends DebugCommand with DebugCommandValidateZeroArgs {
+      object InfoFoundDelimiter extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "foundDelimiter"
         override lazy val short = "fd"
         val desc = "display the current found delimiter"
         val longDesc = desc
+
+        private def getDelimOpt(s: StateForDebugger): Option[String] = {
+          if (s.delimitedParseResult.isDefined) {
+            val pr = s.delimitedParseResult.get
+            val delim = Misc.remapStringToVisibleGlyphs(pr.matchedDelimiterValue.get)
+            Some(delim)
+          } else {
+            None
+          }
+        }
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
-          state match {
-            case pstate: PState => {
-              if (pstate.delimitedParseResult.isDefined) {
-                val pr = pstate.delimitedParseResult.get
-                debugPrintln("%s:".format(name))
-                debugPrintln("foundField: %s".format(Misc.remapStringToVisibleGlyphs(pr.field.get)), "  ")
-                debugPrintln("foundDelimiter: %s".format(Misc.remapStringToVisibleGlyphs(pr.matchedDelimiterValue.get)), "  ")
-              } else {
-                debugPrintln("%s: nothing found".format(name))
-              }
-            }
-            case ustate: UState => {
-              // TODO
+          val delimOpt = getDelimOpt(state)
+          val str = delimOpt.getOrElse("nothing found")
+          debugPrintln("%s: %s".format(name, str))
+          DebugState.Pause
+        }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          val delimOptPre = getDelimOpt(prestate)
+          val delimOptPost = getDelimOpt(state)
+
+          (delimOptPre, delimOptPost) match {
+            case (None, None) => false
+            case (Some(pre), Some(post)) if pre == post => false
+            case (_, _) => {
+              val strPre = delimOptPre.getOrElse("none")
+              val strPost = delimOptPost.getOrElse("none")
+              debugPrintln("%s: %s -> %s".format(name, strPre, strPost), "  ")
+              true
             }
-            case _ => Assert.impossibleCase()
           }
+        }
+      }
+
+      object InfoFoundField extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
+        val name = "foundField"
+        override lazy val short = "ff"
+        val desc = "display the current found field when delimiter scanning"
+        val longDesc = desc
+
+        private def getFieldOpt(s: StateForDebugger): Option[String] = {
+          if (s.delimitedParseResult.isDefined) {
+            val pr = s.delimitedParseResult.get
+            val field = Misc.remapStringToVisibleGlyphs(pr.field.get)
+            Some(field)
+          } else {
+            None
+          }
+        }
+
+        def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
+          val fieldOpt = getFieldOpt(state)
+          val str = fieldOpt.getOrElse("nothing found")
+          debugPrintln("%s: %s".format(name, str))
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          val fieldOptPre = getFieldOpt(prestate)
+          val fieldOptPost = getFieldOpt(state)
+          (fieldOptPre, fieldOptPost) match {
+            case (None, None) => false
+            case (Some(pre), Some(post)) if pre == post => false
+            case (_, _) => {
+              val strPre = fieldOptPre.getOrElse("none")
+              val strPost = fieldOptPost.getOrElse("none")
+              debugPrintln("%s: %s -> %s".format(name, strPre, strPost), "  ")
+              true
+            }
+          }
+        }
       }
 
-      object InfoGroupIndex extends DebugCommand with DebugCommandValidateZeroArgs {
+      object InfoGroupIndex extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "groupIndex"
         override lazy val short = "gi"
         val desc = "display the current group index"
         val longDesc = desc
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
           if (state.groupPos != -1) {
             debugPrintln("%s: %d".format(name, state.groupPos))
@@ -1567,6 +1635,15 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
           }
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          if (prestate.groupPos != state.groupPos) {
+            debugPrintln("%s: %d -> %d".format(name, prestate.groupPos, state.groupPos), "  ")
+            true
+          } else {
+            false
+          }
+        }
       }
 
       object InfoHidden extends DebugCommand with DebugCommandValidateZeroArgs {
@@ -1625,11 +1702,12 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         }
       }
 
-      object InfoOccursIndex extends DebugCommand with DebugCommandValidateZeroArgs {
+      object InfoOccursIndex extends DebugCommand with DebugCommandValidateZeroArgs with InfoDiffable {
         val name = "occursIndex"
         override lazy val short = "oi"
         val desc = "display the current array limit"
         val longDesc = desc
+
         def act(args: Seq[String], prestate: StateForDebugger, state: ParseOrUnparseState, processor: Processor): DebugState.Type = {
           if (state.arrayPos != -1) {
             debugPrintln("%s: %d".format(name, state.arrayPos))
@@ -1638,6 +1716,15 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
           }
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          if (prestate.arrayPos != state.arrayPos) {
+            debugPrintln("%s: %d -> %d".format(name, prestate.arrayPos, state.arrayPos), "  ")
+            true
+          } else {
+            false
+          }
+        }
       }
 
       object InfoPath extends DebugCommand with DebugCommandValidateZeroArgs {
@@ -1692,7 +1779,7 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
         override val name = "unparser"
       } with InfoProcessorBase
 
-      object InfoVariables extends DebugCommand {
+      object InfoVariables extends DebugCommand with InfoDiffable {
         val name = "variables"
         override lazy val short = "v"
         val desc = "display in-scope state of variables"
@@ -1738,6 +1825,29 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
 
           DebugState.Pause
         }
+
+        def diff(prestate: StateForDebugger, state: ParseOrUnparseState): Boolean = {
+          prestate.variableMap.qnames.foldLeft(false) { case (foundDiff, qname) =>
+            val pre_instance = prestate.variableMap.find(qname).get
+            val pre_value = pre_instance.value
+            val pre_state = pre_instance.state
+
+            val cur_instance = state.variableMap.find(qname).get
+            val cur_value = cur_instance.value
+            val cur_state = cur_instance.state
+
+            if (pre_value != cur_value || pre_state != cur_state) {
+              debugPrintln("variable: %s: %s -> %s".format(
+                qname,
+                variableInstanceToDebugString(pre_instance),
+                variableInstanceToDebugString(cur_instance),
+              ), "  ")
+              foundDiff || true
+            } else {
+              foundDiff
+            }
+          }
+        }
       }
     }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorStateBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorStateBases.scala
index 2b0b99f..8327d11 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorStateBases.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorStateBases.scala
@@ -71,23 +71,25 @@ import org.apache.daffodil.dsom.DPathCompileInfo
  * contains member functions for everything the debugger needs to be able to observe.
  */
 trait StateForDebugger {
-  def bytePos: Long
+  def currentLocation: DataLocation
+  def bitPos0b: Long
+  def bitLimit0b: MaybeULong
   def childPos: Long
   def groupPos: Long
-  def currentLocation: DataLocation
   def arrayPos: Long
-  def bitLimit0b: MaybeULong
   def variableMap: VariableMap
+  def delimitedParseResult: Maybe[dfa.ParseResult]
 }
 
 case class TupleForDebugger(
-  val bytePos: Long,
+  val currentLocation: DataLocation,
+  val bitPos0b: Long,
+  val bitLimit0b: MaybeULong,
   val childPos: Long,
   val groupPos: Long,
-  val currentLocation: DataLocation,
   val arrayPos: Long,
-  val bitLimit0b: MaybeULong,
-  val variableMap: VariableMap)
+  val variableMap: VariableMap,
+  val delimitedParseResult: Maybe[dfa.ParseResult])
   extends StateForDebugger
 
 trait SetProcessorMixin {
@@ -427,13 +429,14 @@ abstract class ParseOrUnparseState protected (
 
   def copyStateForDebugger = {
     TupleForDebugger(
-      bytePos,
+      currentLocation,
+      bitPos0b,
+      bitLimit0b,
       childPos,
       groupPos,
-      currentLocation,
       arrayPos,
-      bitLimit0b,
       variableMap.copy(), // deep copy since variableMap is mutable
+      delimitedParseResult,
     )
   }
 
@@ -564,6 +567,7 @@ final class CompileState(tci: DPathCompileInfo, maybeDataProc: Maybe[DataProcess
   def dataStream = Nope
   def groupPos: Long = 0L
   def hasInfoset: Boolean = infoset_.isDefined
+  def delimitedParseResult = Nope
 
   private lazy val infoset_ : Maybe[DIElement] = Nope
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
index 1fb399f..617f484 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
@@ -352,6 +352,8 @@ abstract class UState(
   }
 
   final val releaseUnneededInfoset: Boolean = !areDebugging && tunable.releaseUnneededInfoset
+
+  def delimitedParseResult = Nope
 }
 
 /**