Branch: Tag:

2001-05-08

2001-05-08 00:57:30 by Martin Stjernholm <mast@lysator.liu.se>

Fixed bug in lookup of overridden processing instructions. Fixed [bug
1433 (#1433)] that caused RXML.Frame.propagate_tag to propagate incorrect tag
args/contents in several different circumstances. Updated a couple of
the autodoc comments some more. Added a very simple profilation
system.

Rev: server/etc/modules/RXML.pmod/module.pmod:1.152

1: - // $Id: module.pmod,v 1.151 2001/04/25 14:05:24 nilsson Exp $ + // $Id: module.pmod,v 1.152 2001/05/08 00:57:30 mast Exp $      // Kludge: Must use "RXML.refs" somewhere for the whole module to be   // loaded correctly.
64:   #include <config.h>   #include <request_trace.h>    + // #define PROFILE_PARSER +    #ifdef RXML_OBJ_DEBUG   # define MARK_OBJECT \    Debug.ObjectMarker __object_marker = Debug.ObjectMarker (this_object())
90:   # define OBJ_COUNT ""   #endif    + #ifdef PROFILE_PARSER +  + #define PROFILE_ENTER(ctx, what) do { \ +  ctx->profile[what] -= gethrtime(); \ +  /* if (what == "rxml internal") trace (1); */ \ + } while (0) +  + #define PROFILE_LEAVE(ctx, what) do { \ +  /* if (what == "rxml internal") trace (0); */ \ +  ctx->profile[what] += gethrtime(); \ + } while (0) +  + #define PROFILE_SWITCH(ctx, from, to) do { \ +  /* if (from == "rxml internal") trace (0); */ \ +  int now = gethrtime(); \ +  ctx->profile[from] += now; \ +  ctx->profile[to] -= now; \ +  /* if (to == "rxml internal") trace (1); */ \ + } while (0) +  + #else + # define PROFILE_ENTER(ctx, what) do ; while (0) + # define PROFILE_LEAVE(ctx, what) do ; while (0) + # define PROFILE_SWITCH(ctx, from, to) do ; while (0) + #endif +    #ifdef DEBUG   # define TAG_DEBUG(frame, msg) (frame)->tag_debug ("%O: %s", (frame), (msg))   # define DO_IF_DEBUG(code...) code
113:    //(!) Interface:       //! @decl string name; -  //! The name of the tag. Required and considered constant. +  //! +  //! The name of the tag. Required and considered constant.       /*extern*/ int flags;    //! Various bit flags that affect parsing; see the FLAG_* constants. -  //! RXML.Frame.flags is initialized from this. +  //! @[RXML.Frame.flags] is initialized from this.       mapping(string:Type) req_arg_types = ([]);    mapping(string:Type) opt_arg_types = ([]);
127:       Type def_arg_type = t_text (PEnt);    //! The type used for arguments that isn't present in neither -  //! req_arg_types nor opt_arg_types. This default is a parser that -  //! only parses XML-style entities. +  //! @[req_arg_types] nor @[opt_arg_types]. This default is a parser +  //! that only parses XML-style entities.       Type content_type = t_same (PXml);    //! The handled type of the content, if the tag gets any.    //! -  //! This default is the special type @[RXML.t_same], which means the -  //! type is taken from the effective type of the result. The @[PXml] -  //! argument causes the standard XML parser to be used to read it, -  //! which means that the content is preparsed with XML syntax. Use -  //! no parser to get the raw text. +  //! The default is the special type @[RXML.t_same], which means the +  //! type is taken from the effective type of the result. The +  //! argument to the type is @[RXML.PXml], which causes that parser, +  //! i.e. the standard XML parser, to be used to read it. The effect +  //! is that the content is preparsed with XML syntax. Use no parser, +  //! or @[RXML.PNone], to get the raw text.    //!    //! Note: You probably want to change this to @[RXML.t_text]    //! (without parser) if the tag is a processing instruction (see
160:       //! @decl program Frame;    //! @decl object(Frame) Frame(); +  //!    //! This program/function is used to clone the objects used as -  //! frames. A frame object must (in practice) inherit RXML.Frame. +  //! frames. A frame object must (in practice) inherit @[RXML.Frame].    //! (It can, of course, be any function that requires no arguments    //! and returns a new frame object.) This is not used for plugin    //! tags.       //! @decl string plugin_name; -  +  //!    //! If this is defined, this is a so-called plugin tag. That means -  //! it plugs in some sort of functionality in another Tag object -  //! instead of handling the actual tags of its own. It works as -  //! follows: +  //! it plugs in some sort of functionality in another @[RXML.Tag] +  //! object instead of handling the actual tags of its own. It works +  //! as follows:    //!    //! @list ul    //! @item    //! Instead of installing the callbacks for this tag, the parser -  //! uses another registered "socket" Tag object that got the same -  //! name as this one. Socket tags have the FLAG_SOCKET_TAG flag -  //! set to signify that they accept plugins. +  //! uses another registered "socket" @[Tag] object that got the +  //! same name as this one. Socket tags have the @[FLAG_SOCKET_TAG] +  //! flag set to signify that they accept plugins.    //! @item    //! When the socket tag is parsed or evaluated, it can get the -  //! Tag objects for the registered plugins with the function -  //! Frame.get_plugins(). It's then up to the socket tag to use +  //! @[Tag] objects for the registered plugins with the function +  //! @[Frame.get_plugins]. It's then up to the socket tag to use    //! the plugins according to some API it defines.    //! @item -  //! plugin_name is the name of the plugin. It's used as index in -  //! the mapping that the Frame.get_plugins() returns. +  //! @[plugin_name] is the name of the plugin. It's used as index +  //! in the mapping that the @[Frame.get_plugins] returns.    //! @item    //! The plugin tag is registered in the tag set with the -  //! identifier @code{name + "#" + plugin_name@}. +  //! identifier @code{@[name] + "#" + @[plugin_name]@}.    //!    //! It overrides other plugin tags with that name according to    //! the normal tag set rules, but, as said above, is never    //! registered for actual parsing at all.    //!    //! It's undefined whether plugin tags override normal tags -- -  //! '#' should never be used in normal tag names. +  //! @tt{#@} should never be used in normal tag names.    //! @item    //! It's not an error to register a plugin for which there is no    //! socket. Such plugins are simply ignored.
205:       inline final object/*(Frame)HMM*/ `() (mapping(string:mixed) args, void|mixed content)    //! Make an initialized frame for the tag. Typically useful when -  //! returning generated tags from e.g. RXML.Frame.do_process(). The +  //! returning generated tags from e.g. @[RXML.Frame.do_process]. The    //! argument values and the content are normally not parsed.    {    Tag this = this_object();
220:       int eval_args (mapping(string:mixed) args, void|int dont_throw, void|Context ctx)    //! Parses and evaluates the tag arguments according to -  //! req_arg_types and opt_arg_types. The args mapping contains the -  //! unparsed arguments on entry, and they get replaced by the parsed -  //! results. Arguments not mentioned in req_arg_types or -  //! opt_arg_types are not touched. RXML errors, such as missing -  //! argument, are thrown if dont_throw is zero or left out, -  //! otherwise zero is returned for such errors. ctx specifies the -  //! context to use; it defaults to the current context. +  //! @[req_arg_types] and @[opt_arg_types]. The @[args] mapping +  //! contains the unparsed arguments on entry, and they get replaced +  //! by the parsed results. Arguments not mentioned in +  //! @[req_arg_types] or @[opt_arg_types] are not touched. RXML +  //! errors, such as missing argument, are thrown if @[dont_throw] is +  //! zero or left out, otherwise zero is returned when any such error +  //! occurs. @[ctx] specifies the context to use; it defaults to the +  //! current context.    {    // Note: Code duplication in Frame._eval().    mapping(string:Type) atypes = args & req_arg_types;
420:      class TagSet   //! Contains a set of tags. Tag sets can import other tag sets, and - //! later changes are propagated. Parser instances (contexts) to parse - //! data are created from this. TagSet objects may somewhat safely be - //! destructed explicitly; the tags in a destructed tag set will not - //! be active in parsers that are instantiated later, but will work in - //! current instances. Element (i.e. non-PI) tags and PI tags have - //! separate namespaces. + //! later changes in them are propagated. Parser instances (contexts) + //! to parse data are created from this. @[TagSet] objects may + //! somewhat safely be destructed explicitly; the tags in a destructed + //! tag set will not be active in parsers that are instantiated later, + //! but will work in current instances. Element (i.e. non-PI) tags and + //! PI tags have separate namespaces.   //!   //! @note - //! A Tag object may not be registered in more than one tag set at the - //! same time. + //! An @[RXML.Tag] object may not be registered in more than one tag + //! set at the same time.   {    string name;    //! Used for identification only.       string prefix;    //! A namespace prefix that may precede the tags. If it's zero, it's -  //! up to the importing tag set(s). A ':' is always inserted between -  //! the prefix and the tag name. +  //! up to the importing tag set(s). A @tt{:@} is always inserted +  //! between the prefix and the tag name. +  //! +  //! @note +  //! This namespace scheme is not compliant with the XML namespaces +  //! standard. Since the intention is to implement XML namespaces at +  //! some point, this way of specifying tag prefixes will probably +  //! change.       int prefix_req;    //! The prefix must precede the tags.
449:       function(Context:void) prepare_context;    //! If set, this is a function that will be called before a new -  //! Context object is taken into use. It'll typically prepare -  //! predefined scopes and variables. The functions will be called in -  //! order of precedence; highest last. +  //! @[RXML.Context] object is taken into use. It'll typically +  //! prepare predefined scopes and variables. The functions will be +  //! called in order of precedence; highest last.       int generation = 1;    //! A number that is increased every time something changes in this
520:    }       void remove_tag (string|Tag tag, void|int proc_instr) -  //! If tag is a Tag object, it's removed if this tag set contains -  //! it. If tag is a string, the tag with that name is removed. In -  //! the latter case, if proc_instr is nonzero the set of PI tags is -  //! searched, else the set of normal element tags. +  //! If tag is an @[RXML.Tag] object, it's removed if this tag set +  //! contains it. If tag is a string, the tag with that name is +  //! removed. In the latter case, if @[proc_instr] is nonzero the set +  //! of PI tags is searched, else the set of normal element tags.    {    if (stringp (tag))    if (proc_instr) {
545:    }       local Tag get_local_tag (string name, void|int proc_instr) -  //! Returns the Tag object for the given name in this tag set, if -  //! any. If proc_instr is nonzero the set of PI tags is searched, -  //! else the set of normal element tags. +  //! Returns the @[RXML.Tag] object for the given name in this tag +  //! set, if any. If @[proc_instr] is nonzero the set of PI tags is +  //! searched, else the set of normal element tags.    {    return proc_instr ? proc_instrs && proc_instrs[name] : tags[name];    }       local array(Tag) get_local_tags() -  //! Returns all the Tag objects in this tag set. +  //! Returns all the @[RXML.Tag] objects in this tag set.    {    array(Tag) res = values (tags);    if (proc_instrs) res += values (proc_instrs);
561:    }       local Tag get_tag (string name, void|int proc_instr) -  //! Returns the Tag object for the given name, if any, that's -  //! defined by this tag set (including its imported tag sets). If -  //! proc_instr is nonzero the set of PI tags is searched, else the -  //! set of normal element tags. +  //! Returns the @[RXML.Tag] object for the given name, if any, +  //! that's defined by this tag set (including its imported tag +  //! sets). If @[proc_instr] is nonzero the set of PI tags is +  //! searched, else the set of normal element tags.    {    if (object(Tag) def = get_local_tag (name, proc_instr))    return def;
615:    local array(Tag) get_overridden_tags (string name, void|int proc_instr)    //! Returns all tag definitions for the given name, i.e. including    //! the overridden ones. A tag to the left overrides one to the -  //! right. If proc_instr is nonzero the set of PI tags is searched, -  //! else the set of normal element tags. +  //! right. If @[proc_instr] is nonzero the set of PI tags is +  //! searched, else the set of normal element tags.    {    if (object(Tag) def = get_local_tag (name, proc_instr))    return ({def}) + imported->get_overridden_tags (name, proc_instr) * ({});
626:       void add_string_entities (mapping(string:string) entities)    //! Adds a set of entity replacements that are used foremost by the -  //! @[PXml] parser to decode simple entities like @tt{&amp;@}. The -  //! indices are the entity names without @tt{&@} and @tt{;@}. +  //! @[RXML.PXml] parser to decode simple entities like @tt{&amp;@}. +  //! The indices are the entity names without @tt{&@} and @tt{;@}.    {    if (string_entities) string_entities |= entities;    else string_entities = entities + ([]);
652:    }       local mapping(string:Tag) get_plugins (string name, void|int proc_instr) -  //! Returns the registered plugins for the given tag name. Don't be -  //! destructive on the returned mapping. If proc_instr is nonzero, -  //! the function searches for processing instruction plugins, -  //! otherwise it searches for plugins to normal element tags. +  //! Returns the registered plugins for the given tag name. If +  //! @[proc_instr] is nonzero, the function searches for processing +  //! instruction plugins, otherwise it searches for plugins to normal +  //! element tags. Don't be destructive on the returned mapping.    {    mapping(string:Tag) res;    if (proc_instr) {
824:    {    if (proc_instrs && proc_instrs[overrider_name] == overrider) {    foreach (imported, TagSet tag_set) -  if (object(Tag) overrider = tag_set->get_proc_instr (overrider_name)) +  if (object(Tag) overrider = tag_set->get_tag (overrider_name, 1))    return overrider;    }    else {    int found = 0;    foreach (imported, TagSet tag_set) -  if (object(Tag) subtag = tag_set->get_proc_instr (overrider_name)) +  if (object(Tag) subtag = tag_set->get_tag (overrider_name, 1))    if (found) return subtag;    else if (subtag == overrider)    if ((subtag = tag_set->find_overridden_proc_instr (
895:    //! This is called to get the value of the variable. @[ctx], @[var]    //! and @[scope_name] are set to where this @[Value] object was    //! found. Note that @[scope_name] can be on the form -  //! @tt{scope.index1.index2...@} when this object was encountered +  //! @tt{"scope.index1.index2..."@} when this object was encountered    //! through subindexing. Either @[RXML.nil] or the undefined value    //! may be returned if the variable doesn't have a value.    //!
957:   //!   //! @note   //! The @tt{scope_name@} argument to the functions can be on the form - //! @tt{scope.index1.index2...@} when this object was encountered + //! @tt{"scope.index1.index2..."@} when this object was encountered   //! through subindexing.   {    mixed `[] (string var, void|Context ctx,
1032:   class Context   //! A parser context. This contains the current variable bindings and   //! so on. The current context can always be retrieved with - //! get_context(). + //! @[RXML.get_context].   //!   //! @note   //! Don't store pointers to this object since that will likely   //! introduce circular references. It can be retrieved easily through - //! get_context() or parser->context. + //! @[RXML.get_context].   {    Frame frame;    //! The currently evaluating frame.
1062: Inside #if defined(OLD_RXML_COMPAT)
     #ifdef OLD_RXML_COMPAT    int compatible_scope = 0; -  //! If set, the user_*_var functions access the variables in the -  //! scope "form" by default, and there's no subindex splitting or -  //! ".." decoding is done (see parse_user_var). +  //! If set, the @tt{user_*_var@} functions access the variables in +  //! the scope "form" by default, and there's no subindex splitting +  //! or ".." decoding is done (see @[parse_user_var]). +  //!    //! @note -  //! This is only present when the OLD_RXML_COMPAT define is set. +  //! This is only present when the @tt{OLD_RXML_COMPAT@} define is +  //! set.   #endif    -  + #ifdef PROFILE_PARSER +  mapping(string:int) profile = ([]); + #endif +     array(string|int) parse_user_var (string var, void|string|int scope_name)    //! Parses the var string for scope and/or subindexes according to -  //! the RXML rules, e.g. "scope.var.1.foo". Returns an array where -  //! the first entry is the scope, and the remaining entries are the -  //! list of indexes. If scope_name is a string, it's used as the -  //! scope and the var string is only used for subindexes. A default -  //! scope is chosen as appropriate if var cannot be split, unless -  //! scope_name is a nonzero integer in which case it's returned in -  //! the scope position in the array (useful to detect whether var -  //! actually was splitted or not). +  //! the RXML rules, e.g. @tt{"scope.var.1.foo"@}. Returns an array +  //! where the first entry is the scope, and the remaining entries +  //! are the list of indexes. If @[scope_name] is a string, it's used +  //! as the scope and the var string is only used for subindexes. A +  //! default scope is chosen as appropriate if var cannot be split, +  //! unless @[scope_name] is a nonzero integer in which case it's +  //! returned in the scope position in the array (useful to detect +  //! whether @[var] actually was splitted or not).    //! -  //! A ".." in the var string quotes a literal ".", e.g. -  //! "yow...cons..yet" is separated into "yow." and "cons.yet". Any -  //! subindex that can be parsed as a signed integer is converted to -  //! it. Note that it doesn't happen for the first index, since a -  //! variable in a scope always is a string. +  //! @tt{".."@} in the var string quotes a literal @tt{"."@}, e.g. +  //! @tt{"yow...cons..yet"@} is separated into @tt{"yow."@} and +  //! @tt{"cons.yet"@}. Any subindex that can be parsed as a signed +  //! integer is converted to it. Note that it doesn't happen for the +  //! first index, since a variable in a scope always is a string.    {   #ifdef OLD_RXML_COMPAT    if (compatible_scope && !intp(scope_name))
1118:       local mixed get_var (string|array(string|int) var, void|string scope_name,    void|Type want_type) -  //! Returns the value a variable in the specified scope, or the -  //! current scope if none is given. Returns zero with zero_type 1 if -  //! there's no such variable (or it's nil). +  //! Returns the value of the given variable in the specified scope, +  //! or the current scope if none is given. Returns undefined (zero +  //! with zero type 1) if there's no such variable (or it's +  //! @[RXML.nil]).    //! -  //! If var is an array, it's used to successively index the value to -  //! get subvalues (see @[rxml_index()] for details). +  //! If @[var] is an array, it's used to successively index the value +  //! to get subvalues (see @[rxml_index] for details).    //! -  //! If the want_type argument is set, the result value is converted -  //! to that type with Type.encode(). If the value can't be -  //! converted, an RXML error is thrown. +  //! If the @[want_type] argument is set, the result value is +  //! converted to that type with @[Type.encode]. If the value can't +  //! be converted, an RXML error is thrown.    {   #ifdef MODULE_DEBUG    if (arrayp (var) ? !sizeof (var) : !stringp (var))
1141:    }       mixed user_get_var (string var, void|string scope_name, void|Type want_type) -  //! As get_var, but parses the var string for scope and/or -  //! subindexes, e.g. "scope.var.1.foo" (see parse_user_var for -  //! details). +  //! As @[get_var], but parses the var string for scope and/or +  //! subindexes, e.g. @tt{"scope.var.1.foo"@} (see @[parse_user_var] +  //! for details).    {    if(!var || !sizeof(var)) return ([])[0];    array(string|int) splitted = parse_user_var (var, scope_name);
1152:       local mixed set_var (string|array(string|int) var, mixed val, void|string scope_name)    //! Sets the value of a variable in the specified scope, or the -  //! current scope if none is given. Returns val. +  //! current scope if none is given. Returns @[val].    //! -  //! If var is an array, it's used to successively index the value to -  //! get subvalues (see @[rxml_index()] for details). +  //! If @[var] is an array, it's used to successively index the value +  //! to get subvalues (see @[rxml_index] for details).    {   #ifdef MODULE_DEBUG    if (arrayp (var) ? !sizeof (var) : !stringp (var))
1200:    }       mixed user_set_var (string var, mixed val, void|string scope_name) -  //! As set_var, but parses the var string for scope and/or -  //! subindexes, e.g. "scope.var.1.foo" (see parse_user_var for -  //! details). +  //! As @[set_var], but parses the var string for scope and/or +  //! subindexes, e.g. @tt{"scope.var.1.foo"@} (see @[parse_user_var] +  //! for details).    {    if(!var || !sizeof(var)) parse_error ("No variable specified.\n");    array(string|int) splitted = parse_user_var (var, scope_name);
1213:    //! Removes a variable in the specified scope, or the current scope    //! if none is given.    //! -  //! If var is an array, it's used to successively index the value to -  //! get subvalues (see rxml_index for details). +  //! If @[var] is an array, it's used to successively index the value +  //! to get subvalues (see @[rxml_index] for details).    {   #ifdef MODULE_DEBUG    if (arrayp (var) ? !sizeof (var) : !stringp (var))
1249:    }       void user_delete_var (string var, void|string scope_name) -  //! As delete_var, but parses the var string for scope and/or -  //! subindexes, e.g. "scope.var.1.foo" (see parse_user_var for -  //! details). +  //! As @[delete_var], but parses the var string for scope and/or +  //! subindexes, e.g. @tt{"scope.var.1.foo"@} (see @[parse_user_var] +  //! for details).    {    if(!var || !sizeof(var)) return;    array(string|int) splitted = parse_user_var (var, scope_name);
1285:       void add_scope (string scope_name, SCOPE_TYPE vars)    //! Adds or replaces the specified scope at the global level. A -  //! scope can be a mapping or a Scope object. A global "_" scope may -  //! also be defined this way. +  //! scope can be a mapping or an @[RXML.Scope] object. A global +  //! @tt{"_"@} scope may also be defined this way.    {    if (scopes[scope_name])    if (scope_name == "_") {
1376:    }       void remove_runtime_tag (string|Tag tag, void|int proc_instr) -  //! If tag is a Tag object, it's removed from the set of runtime -  //! tags. If tag is a string, the tag with that name is removed. In -  //! the latter case, if proc_instr is nonzero the set of runtime PI -  //! tags is searched, else the set of normal element runtime tags. +  //! If @[tag] is an @[RXML.Tag] object, it's removed from the set of +  //! runtime tags. If @[tag] is a string, the tag with that name is +  //! removed. In the latter case, if @[proc_instr] is nonzero the set +  //! of runtime PI tags is searched, else the set of normal element +  //! runtime tags.    {    if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags();    if (objectp (tag)) tag = tag->name;
1689:    if (ctx->in_use && ctx->in_use != this_thread()) \    fatal_error ("Attempt to use context asynchronously.\n"); \    ctx->in_use = this_thread(); \ -  } +  } \ +  PROFILE_ENTER (ctx, "rxml internal");      #define LEAVE_CONTEXT() \ -  +  PROFILE_LEAVE (get_context(), "rxml internal"); \    if (Context ctx = get_context()) \    if (__old_ctx != ctx) ctx->in_use = 0; \    set_context (__old_ctx);
1700:      #define ENTER_CONTEXT(ctx) \    Context __old_ctx = get_context(); \ -  set_context (ctx); +  set_context (ctx); \ +  PROFILE_ENTER (ctx, "rxml internal");      #define LEAVE_CONTEXT() \ -  +  PROFILE_LEAVE (get_context(), "rxml internal"); \    set_context (__old_ctx);      #endif
1716:   constant FLAG_DEBUG = 0x40000000;   //! Write a lot of debug during the execution of the tag, showing what   //! type conversions are done, what callbacks are being called etc. - //! Note that DEBUG must be defined for the debug printouts to be - //! compiled in (normally enabled with the --debug flag to Roxen). + //! Note that @tt{DEBUG@} must be defined for the debug printouts to + //! be compiled in (normally enabled with the @tt{--debug@} flag to + //! Roxen).      //(!) Static flags (i.e. tested in the Tag object):      constant FLAG_PROC_INSTR = 0x00000010;   //! Flags this as a processing instruction tag (i.e. one parsed with - //! the <?name ... ?> syntax in XML). The string after the tag name to - //! the ending separator constitutes the content of the tag. Arguments - //! are not used. + //! the @tt{<?name ... ?>@} syntax in XML). The string after the tag + //! name to the ending separator constitutes the content of the tag. + //! Arguments are not used.    - constant FLAG_EMPTY_ELEMENT = 0x00000001; - //! If set, the tag does not use any content. E.g. with an HTML parser - //! this defines whether the tag is a container or not, and in XML - //! parsing the parser will signal an error if the tag have anything - //! but "" as content. -  +    constant FLAG_COMPAT_PARSE = 0x00000002; - //! Makes the PXml parser parse the tag in an HTML compatible way: If - //! FLAG_EMPTY_ELEMENT is set and the tag doesn't end with '/>', it - //! will be parsed as an empty element. The effect of this flag in - //! other parsers is currently undefined. + //! Makes the @[RXML.PXml] parser parse the tag in an HTML compatible + //! way: If @[FLAG_EMPTY_ELEMENT] is set and the tag doesn't end with + //! @tt{"/>"@}, it will be parsed as an empty element. The effect of + //! this flag in other parsers is currently undefined.      constant FLAG_NO_PREFIX = 0x00000004;   //! Never apply any prefix to this tag.      constant FLAG_SOCKET_TAG = 0x00000008;   //! Declare the tag to be a socket tag, which accepts plugin tags (see - //! Tag.plugin_name for details). + //! @[RXML.Tag.plugin_name] for details).      constant FLAG_DONT_PREPARSE = 0x00000040; - //! Don't preparse the content with the PXml parser. This is always - //! the case for PI tags, so this flag doesn't have any effect for - //! those. This is only used in the simple tag wrapper. Defined here - //! as placeholder. + //! Don't preparse the content with the @[RXML.PXml] parser. This is + //! always the case for PI tags, so this flag doesn't have any effect + //! for those. This is only used in the simple tag wrapper. Defined + //! here as placeholder.      constant FLAG_POSTPARSE = 0x00000080; - //! Postparse the result with the PXml parser. This is only used in - //! the simple tag wrapper. Defined here as placeholder. + //! Postparse the result with the @[RXML.PXml] parser. This is only + //! used in the simple tag wrapper. Defined here as placeholder.      //(!) The rest of the flags are dynamic (i.e. tested in the Frame object):    -  + constant FLAG_EMPTY_ELEMENT = 0x00000001; + //! If set, the tag does not use any content. E.g. with an HTML parser + //! this defines whether the tag is a container or not, and in XML + //! parsing the parser will signal an error if the tag have anything + //! but "" as content. Should not be changed after + //! @[RXML.Frame.do_enter] has returned. +    constant FLAG_PARENT_SCOPE = 0x00000100;   //! If set, exec arrays will be interpreted in the scope of the parent   //! tag, rather than in the current one.
1767:   //! yet implemented.      constant FLAG_STREAM_RESULT = 0x00000400; - //! If set, the do_process() function will be called repeatedly until + //! If set, the @[do_process] function will be called repeatedly until   //! it returns 0 or no more content is wanted.      constant FLAG_STREAM_CONTENT = 0x00000800;   //! If set, the tag supports getting its content in streaming mode: - //! do_process() will be called repeatedly with successive parts of the - //! content then. Can't be changed from do_process(). + //! @[do_process] will be called repeatedly with successive parts of + //! the content then. Can't be changed from @[do_process]. + //!   //! @note   //! It might be obvious, but using streaming is significantly less   //! effective than nonstreaming, so it should only be done when big
1782:   constant FLAG_STREAM = FLAG_STREAM_RESULT | FLAG_STREAM_CONTENT;      constant FLAG_UNPARSED = 0x00001000; - //! If set, args and content in the frame contain unparsed strings. - //! The frame will be parsed before it's evaluated. This flag should - //! never be set in Tag.flags, but it's useful when creating frames - //! directly. + //! If set, @[RXML.Frame.args] and @[RXML.Frame.content] contain + //! unparsed strings. The frame will be parsed before it's evaluated. + //! This flag should never be set in @[RXML.Tag.flags], but it's + //! useful when creating frames directly (see @[make_unparsed_tag]).      constant FLAG_DONT_RECOVER = 0x00002000;   //! If set, RXML errors are never recovered when parsing the content
1798:   //! there and continues.   //!   //! The criteria for the frame which will handle the error recovery is - //! that its content type has the free_text property, and that the - //! parser that parses it has an report_error function (which e.g. - //! RXML.PXml has). With this flag, a frame can declare that it isn't - //! suitable to receive error reports even if it satisfies this. + //! that its content type has the @[RXML.Type.free_text] property, and + //! that the parser that parses it has an @[RXML.Parser.report_error] + //! function (which e.g. @[RXML.PXml] has). With this flag, a frame + //! can declare that it isn't suitable to receive error reports even + //! if it satisfies this.      constant FLAG_DONT_REPORT_ERRORS = FLAG_DONT_RECOVER; // For compatibility.   
1811:   //! only encodes the argument quote character with the "Roxen   //! encoding" when writing argument values, instead of encoding with   //! entity references. It's intended for reformatting a tag which has - //! been parsed by Parser.HTML() (or parse_html()) but hasn't been + //! been parsed by @[Parser.HTML] (or @[parse_html]) but hasn't been   //! processed further.      //(!) The following flags specifies whether certain conditions must be
1823:      constant FLAG_CACHE_DIFF_ARGS = 0x00010000;   //! If set, the arguments to the tag need not be the same (using - //! equal()) as the cached args. + //! @[equal]) as the cached args.      constant FLAG_CACHE_DIFF_CONTENT = 0x00020000;   //! If set, the content need not be the same.
2152:    //! the tag after parsing, otherwise the raw_tag_text variable is    //! used, which must have a string value.    { -  if (object(Tag) overridden = get_overridden_tag()) { -  Frame frame; -  if (!args) { +    #ifdef MODULE_DEBUG -  if (zero_type (this_object()->raw_tag_text)) -  fatal_error ("The variable raw_tag_text must be defined.\n"); -  if (!stringp (this_object()->raw_tag_text)) + #define CHECK_RAW_TEXT \ +  if (zero_type (this_object()->raw_tag_text)) \ +  fatal_error ("The variable raw_tag_text must be defined.\n"); \ +  if (!stringp (this_object()->raw_tag_text)) \    fatal_error ("raw_tag_text must have a string value.\n"); -  + #else + #define CHECK_RAW_TEXT   #endif -  +  // FIXME: This assumes an xml-like parser. +  +  if (object(Tag) overridden = get_overridden_tag()) { +  Frame frame; +  if (flags & FLAG_PROC_INSTR) { +  if (!content) { +  CHECK_RAW_TEXT;    if (mixed err = catch { -  Parser_HTML()->_set_tag_callback (lambda (object p) { -  args = p->tag_args(); +  Parser_HTML()->add_quote_tag ( +  "?", +  lambda (object p, string c) { +  sscanf (c, "%*[^ \t\n\r]%s", content);    throw (0); -  +  }, +  "?")->finish (this_object()->raw_tag_text); +  }) throw (err); + #ifdef DEBUG +  if (!stringp (content)) +  fatal_error ("Failed to parse PI tag content for <?%s?> from %O.\n", +  tag->name, this_object()->raw_tag_text); + #endif +  } +  } +  +  else if (!args || !content && !(flags & FLAG_EMPTY_ELEMENT)) { +  CHECK_RAW_TEXT; +  if (mixed err = catch { +  Parser_HTML()->_set_tag_callback ( +  lambda (object p, string s) { +  if (!args) args = p->tag_args(); +  if (content || flags & FLAG_EMPTY_ELEMENT) throw (0); +  else { +  p->_set_tag_callback (0); +  p->add_container (p->tag_name(), +  lambda (object p, mapping a, string c) { +  content = c; +  throw (0); +  }); +  return 1; +  }    })->finish (this_object()->raw_tag_text);    }) throw (err);   #ifdef DEBUG -  if (!mappingp (args)) fatal_error ("Failed to parse tag args from %O.\n", -  this_object()->raw_tag_text); +  if (!mappingp (args)) +  fatal_error ("Failed to parse tag args for <%s> from %O.\n", +  tag->name, this_object()->raw_tag_text); +  if (!stringp (content)) +  fatal_error ("Failed to parse tag content for <%s> from %O.\n", +  tag->name, this_object()->raw_tag_text);   #endif    } -  +     frame = overridden (args, content || "");    frame->flags |= FLAG_UNPARSED;    return frame;    }    -  else -  if (args) return result_type->format_tag (tag, args, content); +     else { - #ifdef MODULE_DEBUG -  if (zero_type (this_object()->raw_tag_text)) -  fatal_error ("The variable raw_tag_text must be defined.\n"); -  if (!stringp (this_object()->raw_tag_text)) -  fatal_error ("raw_tag_text must have a string value.\n"); - #endif -  if (flags & FLAG_PROC_INSTR) +  // Format a new tag, as like the original as possible. FIXME: +  // When it's reformatted, the prefix (if any) won't stick, but +  // it will when it isn't. +  +  if (flags & FLAG_PROC_INSTR) { +  if (content) +  return result_type->format_tag (tag, 0, content); +  else { +  CHECK_RAW_TEXT;    return this_object()->raw_tag_text; -  +  } +  } +     else { -  +  if (args && (content || flags & FLAG_EMPTY_ELEMENT)) +  return result_type->format_tag (tag, args, content); +  else { +  CHECK_RAW_TEXT; +  +  string s;   #ifdef MODULE_DEBUG    if (mixed err = catch {   #endif -  return t_xml (PEnt)->eval (this_object()->raw_tag_text, +  s = t_xml (PEnt)->eval (this_object()->raw_tag_text,    get_context(), empty_tag_set);   #ifdef MODULE_DEBUG    }) {
2201: Inside #if defined(MODULE_DEBUG)
   throw_fatal (err);    }   #endif +  if (!args && !content) return s; +  +  if (mixed err = catch { +  Parser_HTML()->_set_tag_callback ( +  lambda (object p, string s) { +  if (!args) args = p->tag_args(); +  if (content || flags & FLAG_EMPTY_ELEMENT) throw (0); +  else { +  p->_set_tag_callback (0); +  p->add_container (p->tag_name(), +  lambda (object p, mapping a, string c) { +  content = c; +  throw (0); +  }); +  return 1;    } -  +  })->finish (this_object()->raw_tag_text); +  }) throw (err); + #ifdef DEBUG +  if (!mappingp (args)) +  fatal_error ("Failed to parse tag args for <%s> from %O.\n", +  tag->name, this_object()->raw_tag_text); +  if (!stringp (content)) +  fatal_error ("Failed to parse tag content for <%s> from %O.\n", +  tag->name, this_object()->raw_tag_text); + #endif +  return result_type->format_tag (tag, args, content);    }    } -  +  } + #undef CHECK_RAW_TEXT +  }       //(!) Internals:   
2637:    }    else {    THIS_TAG_DEBUG ("Calling do_enter\n"); +  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name);    exec = ([function(RequestID:array)] do_enter) (id); // Might unwind. -  +  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal");    THIS_TAG_DEBUG ((exec ? "Exec array" : "Zero") +    " returned from do_enter\n");    }
2697:    }    else {    THIS_TAG_DEBUG ("Calling do_iterate\n"); +  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name);    iter = (/*[function(RequestID:int)]HMM*/ do_iterate) (    id); // Might unwind. -  +  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal");    THIS_TAG_DEBUG (sprintf ("%O returned from do_iterate\n", iter));    if (ctx->new_runtime_tags)    _handle_runtime_tags (ctx, parser);
2765:    if (!exec) {    THIS_TAG_DEBUG (sprintf ("Iter[%d]: Calling do_process in "    "streaming mode\n", debug_iter)); +  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name);    exec = do_process (id, piece); // Might unwind. -  +  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal");    THIS_TAG_DEBUG (sprintf ("Iter[%d]: %s returned from "    "do_process\n", debug_iter,    exec ? "Exec array" : "Zero"));
2853:    else {    THIS_TAG_DEBUG (sprintf ("Iter[%d]: Calling do_process\n",    debug_iter)); +  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name);    exec = ([function(RequestID,void|mixed:array)] do_process) (    id); // Might unwind. -  +  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal");    THIS_TAG_DEBUG (sprintf ("Iter[%d]: %s returned from do_process\n",    debug_iter, exec ? "Exec array" : "Zero"));    }
2910:    }    else {    THIS_TAG_DEBUG ("Calling do_return\n"); +  PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name);    exec = ([function(RequestID:array)] do_return) (id); // Might unwind. -  +  PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal");    THIS_TAG_DEBUG ((exec ? "Exec array" : "Zero") +    " returned from do_return\n");    }
3456:    throw_fatal (err);    }    LEAVE_CONTEXT(); + #ifdef PROFILE_PARSER +  werror ("Profile for %s: %O\n", context->id->not_query, context->profile); + #endif    }       mixed handle_var (string varref, Type surrounding_type)
3474:    "No scope in variable reference.\n"    "(Use ':' in front to quote a character reference containing dots.)\n");    mixed val; +  PROFILE_SWITCH (context, "rxml internal", "var:" + varref);    if (zero_type (val = context->get_var ( // May throw.    splitted[1..], splitted[0],    encoding ? t_string : surrounding_type)))    val = nil; -  +  PROFILE_SWITCH (context, "var:" + varref, "rxml internal");    if (encoding && !(val = Roxen->roxen_encode (val + "", encoding)))    parse_error ("Unknown encoding %O.\n", encoding);    context->current_var = 0;