sleyzerzon / soar

Automatically exported from code.google.com/p/soar
1 stars 0 forks source link

Add XML Helper functions #6

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Description From Andrew Nuxoll 2005-07-14 16:09:08 (-) [reply]
I'm told that right now, the print()/print_with_symbols() statements are 
still
in the kernel code and that duplicate messages are sent to the debugger via
GenerateMessageXML/GenerateWarningXML/GenerateErrorXML/etc.  I'm told that 
I
should enter a bug report stating that in all of these places there should 
only
be one function call that does both.  So here it is.
------- Comment #1 From Bob Marinier 2008-05-30 18:10:04 (-) [reply] ------
-
Jon and I were discussing this again today.  Currently, what can be done in 
a
single print call usually takes several distinct XML calls (there are a few
helper functions to handle some cases in one call).

Consider this block of code (from decide.cpp), which prints and generates 
XML
for the current apply subphase type (see the print calls to understand 
what's
happening here):

makeAgentCallbackXML(thisAgent, kFunctionBeginTag, kTagSubphase);
makeAgentCallbackXML(thisAgent, kFunctionAddAttribute, kPhase_Name, 
                                kSubphaseName_ChangingWorkingMemory);
switch (thisAgent->FIRING_TYPE) {
   case PE_PRODS:
      print (thisAgent, "\t--- Change Working Memory (PE) ---\n",0);
      makeAgentCallbackXML(thisAgent, kFunctionAddAttribute, 
kPhase_FiringType, 
                                      kPhaseFiringType_PE);
      break;      
   case IE_PRODS:    
      print (thisAgent, "\t--- Change Working Memory (IE) ---\n",0);
      makeAgentCallbackXML(thisAgent, kFunctionAddAttribute, 
kPhase_FiringType, 
                                      kPhaseFiringType_IE);
      break;
}
makeAgentCallbackXML(thisAgent, kFunctionEndTag, kTagSubphase);

There are several things to notice.

1) Two print calls are turned into five XML callbacks.  It would be nice if
there were more/smarter convenience functions so that this could be 
reduced. 
For example, if kPhaseFiringType only occurs in the context of a 
kTagSubphase
tag with a kSubphaseName_ChangingWorkingMemory attribute, then maybe those
could be automatically generated.

In general, we might think about the minimal amount of information 
necessary in
order to generate the required data.  Also, the calls at this level do not
necessarily have to generate literal XML; rather, we just need to generate 
a
structure that can be converted to XML as appropriate (e.g., and ElementXML
object).

The primary issues here are that the XML contains more information than the
print (and thus probably can't be as simple), and that in many places the 
XML
is generated incrementally over many (often recursive) function calls (see
backtrace.cpp, for example; many nested tags are created over several 
calls,
such that there's no way to create the entire root object in one function 
call;
some notion of "begin" and "end" is necessary).

2) The print calls and XML callbacks are done separately.  It would be nice 
if
a single call generated both, or if only XML was generated, and the print
callback  generator produced normal text directly from the XML.  This might 
be
slightly less efficient, but would simplify the code.
------- Comment #2 From Bob Marinier 2008-05-30 20:40:10 (-) [reply] ------
-
Given the right helper functions, the above example could be reduced to:

switch (thisAgent->FIRING_TYPE) {
   case PE_PRODS:
      print (thisAgent, "\t--- Change Working Memory (PE) ---\n",0);
      makeAgentCallbackXML(thisAgent, kTagSubphase, 
                           kPhase_Name, 
kSubphaseName_ChangingWorkingMemory,
                           kPhase_FiringType, kPhaseFiringType_PE);
      break;      
   case IE_PRODS:       
      print (thisAgent, "\t--- Change Working Memory (IE) ---\n",0);
      makeAgentCallbackXML(thisAgent, kTagSubphase,
                           kPhase_Name, 
kSubphaseName_ChangingWorkingMemory,
                           kPhase_FiringType, kPhaseFiringType_IE);
      break;
}

In short, a lot of how easy we can make XML generation depends on whether 
we
can create the right helper functions.  By adding smarts, we may be able to
reduce things even further.  For example, the above example calls could 
even be
reduced to something like this (which would replace the first call):

makeAgentCallbackXML(thisAgent, kPhaseFiringType_IE);

This assumes that this argument alone is sufficient to determine the rest 
of
the arguments.  I suspect that a lot of the code could be reduced this way 
(not
very many tags/attributes are used in multiple contexts).  This would also 
be
much easier if the argument itself was smart (i.e., was an object that knew 
its
complete structure) as opposed to just a string.  That also opens the
possibility of reusing objects in some places (where it's always the same, 
like
here) instead of always creating new ones; this could result in significant
performance gains.

To handle incremental XML construction, we can adopt an interface that is
similar to ElementXML.  For example, we would create an object like this:

makeAgentCallbackXML(thisAgent, kFunctionConstructObject, kTagSubphase);

This assumes that the handler will automatically create the begin and end 
tags
with this information (e.g., it will create an ElementXML object).  Thus, 
we've
eliminated the need to explicitly create the end tag.

The handler would also have to maintain state, so we could do a call like 
this:

makeAgentCallbackXML(thisAgent, kFunctionAddChild, kTagChild);

Subsequent calls that added attributes or children would work on the 
"current"
object, which is the child.  When the child is finished, we get back to the
parent like this:

makeAgentCallbackXML(thisAgent, kFunctionGetParent); // this is like a 
"pop"

This is not quite as easy as printing (straight printing never needs to 
make a
call like this), but I'm not sure there's any way around that (except 
perhaps
by adding yet more helper functions, like "addAttributeAndGetParent", which 
is
a little awkward).

Finally, if requiring the handler to keep state is unacceptable, we can 
keep
state in Soar itself.  We might do this by adding an ElementXML* to the 
agent
structure and directly manipulating it.  We would then pass the completed
ElementXML object to handler.  Practically speaking, I'm not sure there's 
any
real difference: the only handler for these callbacks will be KernelSML, so
it's really just a matter of where we want the code to live.  Possibly it 
will
be more efficient to construct the objects in the kernel directly because 
it
would require far fewer callback calls, which can't be inlined.
------- Comment #3 From Douglas Pearson 2008-05-31 21:32:45 (-) [reply] ---
----
I think it would be a good idea to add some of these smarter helpers.  The
current XML generation code is definitely verbose.
------- Comment #4 From Jonathan Voigt 2009-05-05 14:20:09 (-) [reply] ----

---
*** Bug 1067 has been marked as a duplicate of this bug. ***
------- Comment #5 From Jonathan Voigt 2009-05-05 14:20:24 (-) [reply] ----

---
From duplicate 1067:

Currently, plain print callbacks and XML callbacks are generated separately 
in
the kernel.  This means that everywhere that something is printed, there 
are
two sets of calls: print calls and xml generation calls.  This makes the 
code
harder to read and increases the likelihood of bugs (e.g., places that only
have one of the calls, or that have inconsistent information in the calls).

Since the XML calls contain all of the information in the print calls and 
then
some, one possible solution is to only use the XML calls, and to generate 
plain
print callbacks from the XML information on the fly.  The Java debugger 
already
has code that does this (it only listens for the XML prints in the folding
trace view), so we could adapt that for general use.

The possible disadvantages of this are that generating print callbacks may 
be
slower. But if you're printing, speed is already out the window, so as long 
as
it's within reason, I'm not concerned about this.

A great benefit to this is that people will be forced to generate XML 
messages
for any new prints they add.  (Currently, people often don't bother.)  This
isn't to say that we are necessarily making life more difficult for 
developers.
 If all they want to do is print a simple message, then they can use 
existing
helper functions to do so.  If they want to actually to generated new kinds
structured output, then they will have to make modifications to the print
converter so it knows how to plain print the new stuff.  But they would 
have
had to do that in the debugger anyway.

Original issue reported on code.google.com by voigtjr@gmail.com on 23 Jul 2009 at 4:08

GoogleCodeExporter commented 8 years ago

Original comment by voigtjr@gmail.com on 23 Jul 2009 at 5:29

GoogleCodeExporter commented 8 years ago

Original comment by voigtjr@gmail.com on 23 Feb 2010 at 7:41