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
}
/**