be73272001-06-18Martin Stjernholm // RXML parser and compiler framework. // // Created 1999-07-30 by Martin Stjernholm. //
69e4a42001-08-14Martin Stjernholm // $Id: module.pmod,v 1.222 2001/08/14 17:05:42 mast Exp $
ed81751999-12-11Martin Stjernholm 
151fa92001-04-19Johan Sundström // Kludge: Must use "RXML.refs" somewhere for the whole module to be // loaded correctly.
6c902e2000-03-17Martin Stjernholm static object Roxen;
4becf22000-03-19Martin Nilsson class RequestID { };
ed81751999-12-11Martin Stjernholm 
689dc62000-08-31Martin Stjernholm //! API stability notes: //! //! The API in this file regarding the global functions and the Tag, //! TagSet, Context, Frame and Type classes and their descendants is //! intended to not change in incompatible ways. There are however //! some areas where incompatible changes still must be expected: //!
112b012001-08-10Martin Nilsson //! @list
151fa92001-04-19Johan Sundström //! @item //! The namespace handling will likely change to conform to XML //! namespaces. The currently implemented system is inadequate then //! and will probably be removed. //! @item
12dd772001-07-20Martin Stjernholm //! The RXML parser module currently doesn't stream data according //! to the interface for streaming tags (but the implementation //! still follows the documented API for it). Therefore there's a //! risk that incompatible changes must be made in it due to design //! bugs when it's tested out. That is considered very unlikely, //! though.
151fa92001-04-19Johan Sundström //! @item //! The type system will be developed further, and the API in the //! Type class might change as advanced types gets implemented. //! Don't make assumptions about undocumented behavior. Declare //! data properly with the types RXML.t_xml, RXML.t_html and //! RXML.t_text to let the parser handle the necessary conversions //! instead of doing it yourself. Try to avoid implementing types. //! @item //! Various utilities have FIXME's in their documentation. Needless //! to say they don't work as documented yet, and the doc should be //! considered as ideas only; it might work differently when it's //! actually implemented.
112b012001-08-10Martin Nilsson //! @endlist
689dc62000-08-31Martin Stjernholm //!
151fa92001-04-19Johan Sundström //! @note //! The API for parsers, p-code evaluators etc is not part of the //! "official" API. (The syntax _parsed_ by the currently implemented //! parsers is well defined, of course.)
ed81751999-12-11Martin Stjernholm 
d8769c2000-02-08Martin Stjernholm //#pragma strict_types // Disabled for now since it doesn't work well enough.
ed81751999-12-11Martin Stjernholm 
cd7d5f2000-02-16Martin Stjernholm #include <config.h>
e8dffa2001-05-14Per Hedbor 
b483382000-06-30Martin Stjernholm #include <request_trace.h>
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm #define MAGIC_HELP_ARG // #define OBJ_COUNT_DEBUG
fb605b2001-07-11Martin Stjernholm // #define RXML_OBJ_DEBUG
a08cd72001-06-09Martin Stjernholm // #define RXML_VERBOSE
80e42c2001-06-26Martin Stjernholm // #define RXML_COMPILE_DEBUG
deecca2001-06-28Martin Stjernholm // #define RXML_ENCODE_DEBUG
13a5c32001-07-16Martin Stjernholm // #define TYPE_OBJ_DEBUG
02a6562001-07-09Martin Stjernholm // #define PARSER_OBJ_DEBUG
f853b82001-07-19Martin Stjernholm // #define FRAME_DEPTH_DEBUG
4234122001-08-14Martin Stjernholm // #define RXML_RESCACHE_DEBUG
4293072001-05-08Martin Stjernholm 
e8dffa2001-05-14Per Hedbor 
ec027c2000-03-16Martin Stjernholm #ifdef RXML_OBJ_DEBUG
f02d002000-03-18Martin Stjernholm # define MARK_OBJECT \
fb605b2001-07-11Martin Stjernholm  mapping|object __object_marker = Debug.ObjectMarker (this_object())
f02d002000-03-18Martin Stjernholm # define MARK_OBJECT_ONLY \
fb605b2001-07-11Martin Stjernholm  mapping|object __object_marker = Debug.ObjectMarker (0)
ec027c2000-03-16Martin Stjernholm #else
a529ca2001-07-11Martin Stjernholm # define MARK_OBJECT ; # define MARK_OBJECT_ONLY ;
ec027c2000-03-16Martin Stjernholm #endif
26ff092000-01-21Martin Stjernholm #ifdef OBJ_COUNT_DEBUG // This debug mode gives every object a unique number in the // _sprintf() string.
f02d002000-03-18Martin Stjernholm # ifndef RXML_OBJ_DEBUG # undef MARK_OBJECT # undef MARK_OBJECT_ONLY # define MARK_OBJECT \
fb605b2001-07-11Martin Stjernholm  mapping|object __object_marker = (["count": ++all_constants()->_obj_count])
f02d002000-03-18Martin Stjernholm # define MARK_OBJECT_ONLY \
fb605b2001-07-11Martin Stjernholm  mapping|object __object_marker = (["count": ++all_constants()->_obj_count])
f02d002000-03-18Martin Stjernholm # endif # define OBJ_COUNT (__object_marker ? "[" + __object_marker->count + "]" : "")
26ff092000-01-21Martin Stjernholm #else
f02d002000-03-18Martin Stjernholm # define OBJ_COUNT ""
26ff092000-01-21Martin Stjernholm #endif
d52f8f2001-05-19Martin Stjernholm #ifdef RXML_VERBOSE
09a16e2001-07-10Martin Stjernholm # define TAG_DEBUG_TEST(test) 1
d52f8f2001-05-19Martin Stjernholm #else
4234122001-08-14Martin Stjernholm # define TAG_DEBUG_TEST(test) (test)
d52f8f2001-05-19Martin Stjernholm #endif
ac69772000-09-08Martin Stjernholm #ifdef DEBUG
09a16e2001-07-10Martin Stjernholm # define TAG_DEBUG(frame, msg, args...) \
a529ca2001-07-11Martin Stjernholm  (TAG_DEBUG_TEST (frame && frame->flags & FLAG_DEBUG) && \
09a16e2001-07-10Martin Stjernholm  report_debug ("%O: " + (msg), (frame), args), 0)
7dd3f82001-04-18Martin Stjernholm # define DO_IF_DEBUG(code...) code
ac69772000-09-08Martin Stjernholm #else
4c2b512001-05-17Martin Stjernholm # define TAG_DEBUG(frame, msg, args...) 0
7dd3f82001-04-18Martin Stjernholm # define DO_IF_DEBUG(code...)
ac69772000-09-08Martin Stjernholm #endif
d52f8f2001-05-19Martin Stjernholm #ifdef MODULE_DEBUG # define DO_IF_MODULE_DEBUG(code...) code #else # define DO_IF_MODULE_DEBUG(code...) #endif
f853b82001-07-19Martin Stjernholm #ifdef FRAME_DEPTH_DEBUG # define FRAME_DEPTH_MSG(msg...) report_debug (msg) #else # define FRAME_DEPTH_MSG(msg...) #endif
a529ca2001-07-11Martin Stjernholm #define _LITERAL(X) #X #define LITERAL(X) _LITERAL (X)
f02d002000-03-18Martin Stjernholm #define HASH_INT2(m, n) (n < 65536 ? (m << 16) + n : sprintf ("%x,%x", m, n))
be73272001-06-18Martin Stjernholm #undef RXML_CONTEXT #if constant (thread_create) # define RXML_CONTEXT (_cur_rxml_context->get()) # define SET_RXML_CONTEXT(ctx) (_cur_rxml_context->set (ctx)) #else # define RXML_CONTEXT (_cur_rxml_context) # define SET_RXML_CONTEXT(ctx) (_cur_rxml_context = (ctx)) #endif
7dd3f82001-04-18Martin Stjernholm // Use defines since typedefs doesn't work in soft casts yet. #define SCOPE_TYPE mapping(string:mixed)|object(Scope)
d4768a2001-05-18Martin Stjernholm #define UNWIND_STATE mapping(string|object:mixed|array)
02a6562001-07-09Martin Stjernholm #define EVAL_ARGS_FUNC function(Context,Parser|PCode:mapping(string:mixed))|string
7dd3f82001-04-18Martin Stjernholm 
23a0882000-08-12Martin Stjernholm 
6570bf2001-06-29Martin Stjernholm // Internal caches and object tracking // // This must be first so that it happens early in __INIT. static int tag_set_count = 0;
02a6562001-07-09Martin Stjernholm static mapping(int|string:TagSet) garb_composite_tag_set_cache()
6570bf2001-06-29Martin Stjernholm {
02a6562001-07-09Martin Stjernholm  call_out (garb_composite_tag_set_cache, 30*60); return composite_tag_set_cache = ([]);
6570bf2001-06-29Martin Stjernholm }
02a6562001-07-09Martin Stjernholm static mapping(int|string:TagSet) composite_tag_set_cache = garb_composite_tag_set_cache(); #define GET_COMPOSITE_TAG_SET(a, b, res) do { \ int|string hash = HASH_INT2 (b->id_number, a->id_number); \ if (!(res = composite_tag_set_cache[hash])) { \
59b0562001-07-11Martin Stjernholm  res = shared_tag_set ( \ a->name && b->name && \ sprintf ("[%d]%s+%s", sizeof (a->name), a->name, b->name)); \
02a6562001-07-09Martin Stjernholm  res->imported = ({a, b}); \ /* Race, but it doesn't matter. */ \ composite_tag_set_cache[hash] = res; \ } \ } while (0)
6570bf2001-06-29Martin Stjernholm 
7e0fda2001-07-12Martin Stjernholm static mapping(string:TagSet|int) all_tagsets = set_weak_flag(([]), 1); // Maps all tag set names to their TagSet objects. // // For tag sets that have been removed, the value is their generation // number, so that a new tag set with that name will continue the // generation sequence. We use the fact that a weak mapping won't // remove items that aren't refcounted (and strings).
6570bf2001-06-29Martin Stjernholm  static mapping(string:program/*(Parser)*/) reg_parsers = ([]); // Maps each parser name to the parser program. static mapping(string:Type) reg_types = ([]); // Maps each type name to a type object with the PNone parser. static mapping(mixed:string) reverse_constants = set_weak_flag (([]), 1); // Interface classes
ed81751999-12-11Martin Stjernholm class Tag //! Interface class for the static information about a tag. {
cece9e2000-01-11Martin Stjernholm  constant is_RXML_Tag = 1;
c1ee002001-06-29Martin Stjernholm  constant is_RXML_encodable = 1;
02a6562001-07-09Martin Stjernholm  constant is_RXML_p_code_frame = 1;
cf042f2001-07-21Martin Stjernholm  constant is_RXML_p_code_entry = 1;
cece9e2000-01-11Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm  // Interface:
ed81751999-12-11Martin Stjernholm 
151fa92001-04-19Johan Sundström  //! @decl string name;
4293072001-05-08Martin Stjernholm  //! //! The name of the tag. Required and considered constant.
ed81751999-12-11Martin Stjernholm 
fc75492001-06-22Marcus Comstedt  TagSet tagset; //! The tag set that this tag belongs to, if any.
f585722000-01-18Martin Stjernholm  /*extern*/ int flags;
ed81751999-12-11Martin Stjernholm  //! Various bit flags that affect parsing; see the FLAG_* constants.
4293072001-05-08Martin Stjernholm  //! @[RXML.Frame.flags] is initialized from this.
ed81751999-12-11Martin Stjernholm 
710b482000-02-07Martin Stjernholm  mapping(string:Type) req_arg_types = ([]); mapping(string:Type) opt_arg_types = ([]); //! The names and types of the required and optional arguments. If a //! type specifies a parser, it'll be used on the argument value. //! Note that the order in which arguments are parsed is arbitrary.
ed81751999-12-11Martin Stjernholm 
f55a012001-03-14Martin Stjernholm  Type def_arg_type = t_text (PEnt);
3187d82000-01-28Martin Stjernholm  //! The type used for arguments that isn't present in neither
4293072001-05-08Martin Stjernholm  //! @[req_arg_types] nor @[opt_arg_types]. This default is a parser //! that only parses XML-style entities.
3187d82000-01-28Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm  Type content_type = t_same (PXml);
590b3c2000-02-05Martin Stjernholm  //! The handled type of the content, if the tag gets any.
ed81751999-12-11Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! 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.
24f9e82000-08-05Martin Stjernholm  //!
f55a012001-03-14Martin Stjernholm  //! Note: You probably want to change this to @[RXML.t_text]
0c9e5b2001-03-13Martin Stjernholm  //! (without parser) if the tag is a processing instruction (see //! @[FLAG_PROC_INSTR]).
ed81751999-12-11Martin Stjernholm 
be73272001-06-18Martin Stjernholm  array(Type) result_types = ({t_string, t_any_text});
c6245b1999-12-31Martin Stjernholm  //! The possible types of the result, in order of precedence. If a
91c8982000-03-11Martin Stjernholm  //! result type has a parser, it'll be used to parse any strings in
0c9e5b2001-03-13Martin Stjernholm  //! the exec array returned from @[Frame.do_enter] and similar
91c8982000-03-11Martin Stjernholm  //! callbacks.
46c68f2000-08-12Martin Stjernholm  //!
0c9e5b2001-03-13Martin Stjernholm  //! When the tag is used in content of some type, the content type //! may be a supertype of any type in @[result_types], but it may //! also be a subtype of any of them. The tag must therefore be //! prepared to produce result of more specific types than those //! declared here. I.e. the extreme case, @[RXML.t_any], means that //! this tag takes the responsibility to produce result of any type //! that's asked for, not that it has the liberty to produce results //! of any type it chooses.
be73272001-06-18Martin Stjernholm  //! //! The types in this list is first searched in order for a type //! that is a subtype of the actual type. If none is found, the list //! is searched through a second time for a type that is a supertype //! of the actual type. //! //! The default value inherited from this class defines the tag to //! produce any string result (and thereby accept any string //! content, due to @[RXML.t_same] in @[content_type]). If the type //! is @tt{text/*@} (@[RXML.t_any_text]) or some subtype, the tag //! operates on text, which means all whitespace is significant. If //! the type is @tt{string@} or some supertype, the tag operates on //! the @tt{string@} type (@[RXML.t_string]) which ignores //! whitespace between tokens (the tag doesn't need to do any //! special treatment of this).
ed81751999-12-11Martin Stjernholm 
151fa92001-04-19Johan Sundström  //! @decl program Frame; //! @decl object(Frame) Frame();
4293072001-05-08Martin Stjernholm  //!
c159ec2000-02-04Martin Stjernholm  //! This program/function is used to clone the objects used as
4293072001-05-08Martin Stjernholm  //! frames. A frame object must (in practice) inherit @[RXML.Frame].
c159ec2000-02-04Martin Stjernholm  //! (It can, of course, be any function that requires no arguments //! and returns a new frame object.) This is not used for plugin //! tags.
151fa92001-04-19Johan Sundström  //! @decl string plugin_name;
4293072001-05-08Martin Stjernholm  //!
c159ec2000-02-04Martin Stjernholm  //! If this is defined, this is a so-called plugin tag. That means
4293072001-05-08Martin Stjernholm  //! 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:
c159ec2000-02-04Martin Stjernholm  //!
112b012001-08-10Martin Nilsson  //! @list
151fa92001-04-19Johan Sundström  //! @item //! Instead of installing the callbacks for this tag, the parser
4293072001-05-08Martin Stjernholm  //! 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.
151fa92001-04-19Johan Sundström  //! @item //! When the socket tag is parsed or evaluated, it can get the
4293072001-05-08Martin Stjernholm  //! @[Tag] objects for the registered plugins with the function //! @[Frame.get_plugins]. It's then up to the socket tag to use
151fa92001-04-19Johan Sundström  //! the plugins according to some API it defines. //! @item
4293072001-05-08Martin Stjernholm  //! @[plugin_name] is the name of the plugin. It's used as index //! in the mapping that the @[Frame.get_plugins] returns.
151fa92001-04-19Johan Sundström  //! @item //! The plugin tag is registered in the tag set with the
4293072001-05-08Martin Stjernholm  //! identifier @code{@[name] + "#" + @[plugin_name]@}.
c159ec2000-02-04Martin Stjernholm  //!
151fa92001-04-19Johan Sundström  //! 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.
c159ec2000-02-04Martin Stjernholm  //!
151fa92001-04-19Johan Sundström  //! It's undefined whether plugin tags override normal tags --
4293072001-05-08Martin Stjernholm  //! @tt{#@} should never be used in normal tag names.
151fa92001-04-19Johan Sundström  //! @item //! It's not an error to register a plugin for which there is no //! socket. Such plugins are simply ignored. //! @endlist
ed81751999-12-11Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm  // Services:
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  final object/*(Frame)HMM*/ `() (mapping(string:mixed) args, void|mixed content)
ed81751999-12-11Martin Stjernholm  //! Make an initialized frame for the tag. Typically useful when
4293072001-05-08Martin Stjernholm  //! returning generated tags from e.g. @[RXML.Frame.do_process]. The
cf3c902000-02-04Martin Stjernholm  //! argument values and the content are normally not parsed.
ed81751999-12-11Martin Stjernholm  {
02a6562001-07-09Martin Stjernholm  object/*(Frame)HMM*/ frame = ([function(:object/*(Frame)HMM*/)] this_object()->Frame)(); frame->tag = this_object();
ed81751999-12-11Martin Stjernholm  frame->flags = flags; frame->args = args;
23a0882000-08-12Martin Stjernholm  frame->content = zero_type (content) ? nil : content;
ed81751999-12-11Martin Stjernholm  return frame; }
3704fa2001-06-09Martin Nilsson  int eval_args (mapping(string:mixed) args, void|int dont_throw, void|Context ctx, void|array(string) ignore_args)
c159ec2000-02-04Martin Stjernholm  //! Parses and evaluates the tag arguments according to
4293072001-05-08Martin Stjernholm  //! @[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
3704fa2001-06-09Martin Nilsson  //! @[req_arg_types] or @[opt_arg_types] are evaluated with the default //! argument type, unless listed in @[ignore_args]. 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.
c159ec2000-02-04Martin Stjernholm  {
a529ca2001-07-11Martin Stjernholm  // Note: Approximate code duplication in _eval_splice_args and // Frame._prepare.
710b482000-02-07Martin Stjernholm  mapping(string:Type) atypes = args & req_arg_types; if (sizeof (atypes) < sizeof (req_arg_types)) if (dont_throw) return 0; else { array(string) missing = sort (indices (req_arg_types - atypes));
5ed0132000-02-13Martin Stjernholm  parse_error ("Required " + (sizeof (missing) > 1 ? "arguments " + String.implode_nicely (missing) + " are" : "argument " + missing[0] + " is") + " missing.\n");
710b482000-02-07Martin Stjernholm  } atypes += args & opt_arg_types;
c159ec2000-02-04Martin Stjernholm #ifdef MODULE_DEBUG
710b482000-02-07Martin Stjernholm  if (mixed err = catch {
c159ec2000-02-04Martin Stjernholm #endif
3704fa2001-06-09Martin Nilsson  foreach (indices (args) - ( ignore_args||({}) ), string arg)
a08cd72001-06-09Martin Stjernholm  args[arg] = (atypes[arg] || def_arg_type)->eval ( args[arg], ctx); // Should not unwind.
c159ec2000-02-04Martin Stjernholm #ifdef MODULE_DEBUG
710b482000-02-07Martin Stjernholm  }) { if (objectp (err) && ([object] err)->thrown_at_unwind)
8cb24d2000-02-13Martin Stjernholm  fatal_error ("Can't save parser state when evaluating arguments.\n"); throw_fatal (err);
c159ec2000-02-04Martin Stjernholm  }
710b482000-02-07Martin Stjernholm #endif
c159ec2000-02-04Martin Stjernholm  return 1; }
5c7a6b2001-06-25Martin Stjernholm  // Internals:
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm #define MAKE_FRAME(_frame, _ctx, _parser, _args, _content) \
7dd3f82001-04-18Martin Stjernholm  make_new_frame: do { \
a08cd72001-06-09Martin Stjernholm  if (UNWIND_STATE ustate = _ctx->unwind_state) \ if (ustate[_parser]) { \ _frame = [object/*(Frame)HMM*/] ustate[_parser][0]; \ m_delete (ustate, _parser); \ if (!sizeof (ustate)) _ctx->unwind_state = 0; \
7dd3f82001-04-18Martin Stjernholm  break make_new_frame; \ } \
02a6562001-07-09Martin Stjernholm  _frame = \ ([function(:object/*(Frame)HMM*/)] this_object()->Frame)(); \ _frame->tag = this_object(); \ _frame->flags = flags|FLAG_UNPARSED; \ _frame->args = _args; \ _frame->content = _content || ""; \
4c2b512001-05-17Martin Stjernholm  DO_IF_DEBUG( \
079f362001-06-29Martin Stjernholm  if (_args && ([mapping] (mixed) _args)["_debug_"]) { \
a08cd72001-06-09Martin Stjernholm  _frame->flags |= FLAG_DEBUG; \
079f362001-06-29Martin Stjernholm  m_delete (_args, "_debug_"); \
4c2b512001-05-17Martin Stjernholm  } \ ); \
7dd3f82001-04-18Martin Stjernholm  } while (0)
02a6562001-07-09Martin Stjernholm #define EVAL_FRAME(_frame, _ctx, _parser, _type, _res) \ do { \
446bfa2001-06-21Martin Stjernholm  EVAL_ARGS_FUNC argfunc = 0; \
02a6562001-07-09Martin Stjernholm  if (mixed err = catch { \ _res = _frame->_eval (_ctx, _parser, _type); \
fb605b2001-07-11Martin Stjernholm  if (PCode p_code = _parser->p_code) \
6dfc812001-07-20Martin Stjernholm  p_code->add_frame (_ctx, _frame, _res); \ }) \
02a6562001-07-09Martin Stjernholm  if (objectp (err) && ([object] err)->thrown_at_unwind) { \ UNWIND_STATE ustate = _ctx->unwind_state; \ if (!ustate) ustate = _ctx->unwind_state = ([]); \ DO_IF_DEBUG ( \ if (err != _frame) \
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Unexpected unwind object catched.\n"); \
02a6562001-07-09Martin Stjernholm  if (ustate[_parser]) \
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Clobbering unwind state for parser.\n"); \
02a6562001-07-09Martin Stjernholm  ); \ ustate[_parser] = ({_frame}); \ throw (_parser); \ } \ else { \
6dfc812001-07-20Martin Stjernholm  if (PCode p_code = _parser->p_code) \ p_code->add_frame (_ctx, _frame, PCode); \ throw (err); \
02a6562001-07-09Martin Stjernholm  } \
7dd3f82001-04-18Martin Stjernholm  } while (0) final mixed handle_tag (TagSetParser parser, mapping(string:string) args, void|string content) // Callback for tag set parsers to handle tags. Note that this // function handles an unwind frame for the parser. { // Note: args may be zero when this is called for PI tags. Context ctx = parser->context; object/*(Frame)HMM*/ frame;
02a6562001-07-09Martin Stjernholm  MAKE_FRAME (frame, ctx, parser, args, content);
7dd3f82001-04-18Martin Stjernholm  if (!zero_type (frame->raw_tag_text)) frame->raw_tag_text = parser->raw_tag_text(); mixed result;
02a6562001-07-09Martin Stjernholm  EVAL_FRAME (frame, ctx, parser, parser->type, result);
7dd3f82001-04-18Martin Stjernholm  return result; } final array _p_xml_handle_tag (object/*(PXml)*/ parser, mapping(string:string) args, void|string content)
ed81751999-12-11Martin Stjernholm  {
7dd3f82001-04-18Martin Stjernholm  Type type = parser->type;
446bfa2001-06-21Martin Stjernholm  parser->drain_output();
56532d1999-12-19Martin Stjernholm  Context ctx = parser->context;
f997202000-01-18Martin Stjernholm  object/*(Frame)HMM*/ frame;
02a6562001-07-09Martin Stjernholm  MAKE_FRAME (frame, ctx, parser, args, content);
7dd3f82001-04-18Martin Stjernholm  if (!zero_type (frame->raw_tag_text))
a08cd72001-06-09Martin Stjernholm  frame->raw_tag_text = parser->current_input();
7dd3f82001-04-18Martin Stjernholm  mixed result;
02a6562001-07-09Martin Stjernholm  EVAL_FRAME (frame, ctx, parser, type, result);
446bfa2001-06-21Martin Stjernholm  if (result != nil) parser->add_value (result);
7dd3f82001-04-18Martin Stjernholm  return ({});
ed81751999-12-11Martin Stjernholm  }
7dd3f82001-04-18Martin Stjernholm  final array _p_xml_handle_pi_tag (object/*(PXml)*/ parser, string content)
24f9e82000-08-05Martin Stjernholm  {
7dd3f82001-04-18Martin Stjernholm  Type type = parser->type;
446bfa2001-06-21Martin Stjernholm  parser->drain_output();
7dd3f82001-04-18Martin Stjernholm 
46c68f2000-08-12Martin Stjernholm  sscanf (content, "%[ \t\n\r]%s", string ws, string rest);
7dd3f82001-04-18Martin Stjernholm  if (ws == "" && rest != "") {
24f9e82000-08-05Martin Stjernholm  // The parser didn't match a complete name, so this is a false // alarm for an unknown PI tag.
7dd3f82001-04-18Martin Stjernholm  if (!type->free_text) return utils->unknown_pi_tag_error (parser, content);
24f9e82000-08-05Martin Stjernholm  return 0;
7dd3f82001-04-18Martin Stjernholm  }
24f9e82000-08-05Martin Stjernholm 
7dd3f82001-04-18Martin Stjernholm  Context ctx = parser->context; object/*(Frame)HMM*/ frame;
02a6562001-07-09Martin Stjernholm  MAKE_FRAME (frame, ctx, parser, 0, content);
7dd3f82001-04-18Martin Stjernholm  if (!zero_type (frame->raw_tag_text)) frame->raw_tag_text = parser->current_input(); mixed result;
02a6562001-07-09Martin Stjernholm  EVAL_FRAME (frame, ctx, parser, type, result);
446bfa2001-06-21Martin Stjernholm  if (result != nil) parser->add_value (result);
7dd3f82001-04-18Martin Stjernholm  return ({});
87fb7e2000-03-25Martin Stjernholm  }
a529ca2001-07-11Martin Stjernholm  mapping(string:mixed) _eval_splice_args (Context ctx, mapping(string:string) raw_args, mapping(string:Type) my_req_args) // Used from Frame._prepare for evaluating the dynamic arguments in // the splice argument. Destructive on raw_args. { // Note: Approximate code duplication in eval_args and Frame._prepare. mapping(string:Type) atypes = raw_args & (req_arg_types | opt_arg_types); if (my_req_args) { mapping(string:Type) missing = my_req_args - atypes; if (sizeof (missing)) parse_error ("Required " + (sizeof (missing) > 1 ? "arguments " + String.implode_nicely ( sort (indices (missing))) + " are" : "argument " + indices (missing)[0] + " is") + " missing.\n"); } #ifdef MODULE_DEBUG if (mixed err = catch { #endif foreach (indices (raw_args), string arg) { Type t = atypes[arg] || def_arg_type; if (t->parser_prog != PNone) { Parser parser = t->get_parser (ctx, ctx->tag_set, 0); TAG_DEBUG (RXML_CONTEXT->frame, "Evaluating argument value %s with %O\n",
a835e82001-07-25Martin Stjernholm  format_short (raw_args[arg]), parser);
a529ca2001-07-11Martin Stjernholm  parser->finish (raw_args[arg]); // Should not unwind. raw_args[arg] = parser->eval(); // Should not unwind. TAG_DEBUG (RXML_CONTEXT->frame, "Setting dynamic argument %s to %s\n",
a835e82001-07-25Martin Stjernholm  format_short (arg), format_short (raw_args[arg]));
a529ca2001-07-11Martin Stjernholm  t->give_back (parser, ctx->tag_set); } } #ifdef MODULE_DEBUG }) { if (objectp (err) && ([object] err)->thrown_at_unwind) fatal_error ("Can't save parser state when evaluating dynamic arguments.\n"); throw_fatal (err); } #endif return raw_args; }
112b012001-08-10Martin Nilsson  //! @ignore
f02d002000-03-18Martin Stjernholm  MARK_OBJECT;
112b012001-08-10Martin Nilsson  //! @endignore
26ff092000-01-21Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  string _sprintf() {
24f9e82000-08-05Martin Stjernholm  return "RXML.Tag(" + [string] this_object()->name + (this_object()->plugin_name ? "#" + [string] this_object()->plugin_name : "") + ([int] this_object()->flags & FLAG_PROC_INSTR ? " [PI]" : "") + ")" + OBJ_COUNT;
ed81751999-12-11Martin Stjernholm  } } class TagSet //! Contains a set of tags. Tag sets can import other tag sets, and
4293072001-05-08Martin Stjernholm //! 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.
24f9e82000-08-05Martin Stjernholm //!
151fa92001-04-19Johan Sundström //! @note
4293072001-05-08Martin Stjernholm //! An @[RXML.Tag] object may not be registered in more than one tag //! set at the same time.
ed81751999-12-11Martin Stjernholm {
c1ee002001-06-29Martin Stjernholm  constant is_RXML_TagSet = 1;
2bd21a2000-01-05Martin Stjernholm  string name;
deecca2001-06-28Martin Stjernholm  //! Unique identification string. Must be stable across server //! restarts since it's used to identify tag sets in dumped p-code.
02a6562001-07-09Martin Stjernholm  //! The name may contain the characters "!", "#", "(", ")", ",", //! "-", ".", "/", ":", ";", "<", "=", ">", "?", "@@", "_", and any //! alphanumeric character.
2bd21a2000-01-05Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  string prefix;
f997202000-01-18Martin Stjernholm  //! A namespace prefix that may precede the tags. If it's zero, it's
4293072001-05-08Martin Stjernholm  //! 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.
ed81751999-12-11Martin Stjernholm 
f997202000-01-18Martin Stjernholm  int prefix_req;
ed81751999-12-11Martin Stjernholm  //! The prefix must precede the tags. array(TagSet) imported = ({}); //! Other tag sets that will be used. The precedence is local tags //! first, then imported from left to right. It's not safe to //! destructively change entries in this array.
936f632001-07-16Martin Stjernholm  //! //! @note //! The return value from @[get_hash] depends on the exact order in //! this array. So even if the order isn't important for tag //! overriding it should not be random in any way, or else //! @[get_hash] won't return stable values. That would in turn make //! decoding with @[RXML.string_to_p_code] fail almost always.
ed81751999-12-11Martin Stjernholm 
660cb52000-01-12Martin Stjernholm  function(Context:void) prepare_context; //! If set, this is a function that will be called before a new
4293072001-05-08Martin Stjernholm  //! @[RXML.Context] object is taken into use. It'll typically
be73272001-06-18Martin Stjernholm  //! prepare predefined scopes and variables. The callbacks in //! imported tag sets will be called in order of precedence; highest //! last. function(Context:void) eval_finish; //! If set, this will be called just before an evaluation of the //! given @[RXML.Context] finishes. The callbacks in imported tag //! sets will be called in order of precedence; highest last.
660cb52000-01-12Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  int generation = 1; //! A number that is increased every time something changes in this //! object or in some tag set it imports.
f02d002000-03-18Martin Stjernholm  int id_number; //! Unique number identifying this tag set.
2bd21a2000-01-05Martin Stjernholm  static void create (string _name, void|array(Tag) _tags)
ed81751999-12-11Martin Stjernholm  //! {
f02d002000-03-18Martin Stjernholm  id_number = ++tag_set_count;
c1ac6c2001-07-02Martin Stjernholm  set_name (_name);
24f9e82000-08-05Martin Stjernholm  if (_tags) add_tags (_tags);
f02d002000-03-18Martin Stjernholm #ifdef RXML_OBJ_DEBUG __object_marker->create (this_object()); #endif
ed81751999-12-11Martin Stjernholm  } void add_tag (Tag tag) //! {
e821042000-08-17Martin Stjernholm #ifdef MODULE_DEBUG if (!stringp (tag->name)) error ("Trying to register a tag %O without a name.\n", tag);
055e932001-04-09Per Hedbor  if (!callablep (tag->Frame) && !tag->plugin_name)
e821042000-08-17Martin Stjernholm  error ("Trying to register a tag %O without a Frame class or function.\n", tag);
1a65d92000-08-30Martin Stjernholm  if (tag->name[..3] != "!--#" && // Ugly special case for SSI tags. replace (tag->name, "#<>& \t\n\r" / "", ({""}) * 8) != tag->name) error ("Invalid character(s) in name for tag %O.\n", tag);
e821042000-08-17Martin Stjernholm #endif
24f9e82000-08-05Martin Stjernholm  if (tag->flags & FLAG_PROC_INSTR) { if (!proc_instrs) proc_instrs = ([]); if (tag->plugin_name) proc_instrs[tag->name + "#" + tag->plugin_name] = tag; else proc_instrs[tag->name] = tag; } else if (tag->plugin_name) tags[tag->name + "#" + tag->plugin_name] = tag; else tags[tag->name] = tag;
ed81751999-12-11Martin Stjernholm  changed(); } void add_tags (array(Tag) _tags) //! {
24f9e82000-08-05Martin Stjernholm  foreach (_tags, Tag tag) {
e821042000-08-17Martin Stjernholm #ifdef MODULE_DEBUG if (!stringp (tag->name)) error ("Trying to register a tag %O without a name.\n", tag);
055e932001-04-09Per Hedbor  if (!callablep (tag->Frame)&& !tag->plugin_name)
e821042000-08-17Martin Stjernholm  error ("Trying to register a tag %O without a Frame class or function.\n", tag);
1a65d92000-08-30Martin Stjernholm  if (tag->name[..3] != "!--#" && // Ugly special case for SSI tags. replace (tag->name, "#<>& \t\n\r" / "", ({""}) * 8) != tag->name) error ("Invalid character(s) in name for tag %O.\n", tag);
e821042000-08-17Martin Stjernholm #endif
24f9e82000-08-05Martin Stjernholm  if (tag->flags & FLAG_PROC_INSTR) { if (!proc_instrs) proc_instrs = ([]); if (tag->plugin_name) proc_instrs[tag->name + "#" + tag->plugin_name] = tag; else proc_instrs[tag->name] = tag; } else if (tag->plugin_name) tags[tag->name + "#" + tag->plugin_name] = tag; else tags[tag->name] = tag;
fc75492001-06-22Marcus Comstedt  tag->tagset = this_object();
24f9e82000-08-05Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm  changed(); }
24f9e82000-08-05Martin Stjernholm  void remove_tag (string|Tag tag, void|int proc_instr)
4293072001-05-08Martin Stjernholm  //! 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.
ed81751999-12-11Martin Stjernholm  { if (stringp (tag))
24f9e82000-08-05Martin Stjernholm  if (proc_instr) { if (proc_instrs) m_delete (proc_instrs, tag); } else m_delete (tags, tag); else { string n; if (tag->flags & FLAG_PROC_INSTR) { if (proc_instrs && !zero_type (n = search (tags, [object(Tag)] tag))) m_delete (proc_instrs, n); } else if (!zero_type (n = search (tags, [object(Tag)] tag))) m_delete (tags, n); }
ed81751999-12-11Martin Stjernholm  changed(); }
2aa1dc2001-08-11Martin Stjernholm  void clear() //! Removes all registered tags, processing instructions and string //! entities. { tags = ([]), proc_instrs = 0; clear_string_entities(); // Calls changed(). }
24f9e82000-08-05Martin Stjernholm  local Tag get_local_tag (string name, void|int proc_instr)
4293072001-05-08Martin Stjernholm  //! 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.
ed81751999-12-11Martin Stjernholm  {
24f9e82000-08-05Martin Stjernholm  return proc_instr ? proc_instrs && proc_instrs[name] : tags[name];
ed81751999-12-11Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm  local array(Tag) get_local_tags()
4293072001-05-08Martin Stjernholm  //! Returns all the @[RXML.Tag] objects in this tag set.
ed81751999-12-11Martin Stjernholm  {
24f9e82000-08-05Martin Stjernholm  array(Tag) res = values (tags); if (proc_instrs) res += values (proc_instrs); return res;
ed81751999-12-11Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm  local Tag get_tag (string name, void|int proc_instr)
4293072001-05-08Martin Stjernholm  //! 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.
2bd21a2000-01-05Martin Stjernholm  {
24f9e82000-08-05Martin Stjernholm  if (object(Tag) def = get_local_tag (name, proc_instr))
56ffee2000-01-12Martin Stjernholm  return def; foreach (imported, TagSet tag_set)
24f9e82000-08-05Martin Stjernholm  if (object(Tag) tag = [object(Tag)] tag_set->get_tag (name, proc_instr)) return tag;
56ffee2000-01-12Martin Stjernholm  return 0;
da54bd2000-01-08Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm  local int has_tag (Tag tag) //! Returns nonzero if the given tag is contained in this tag set //! (including its imported tag sets). { return !!get_tag (tag->name, tag->flags & FLAG_PROC_INSTR); } local multiset(string) get_tag_names()
24f9e82000-08-05Martin Stjernholm  //! Returns the names of all non-PI tags that this tag set defines. { return `| ((multiset) indices (tags), @imported->get_tag_names()); }
689dc62000-08-31Martin Stjernholm  local multiset(string) get_proc_instr_names()
24f9e82000-08-05Martin Stjernholm  //! Returns the names of all PI tags that this tag set defines. { return `| (proc_instrs ? (multiset) indices (proc_instrs) : (<>), @imported->get_proc_instr_names()); }
689dc62000-08-31Martin Stjernholm  local Tag get_overridden_tag (Tag overrider)
56ffee2000-01-12Martin Stjernholm  //! Returns the tag definition that the given one overrides, or zero
2e0a6d2000-07-06Martin Stjernholm  //! if none.
da54bd2000-01-08Martin Stjernholm  {
51f88b2000-06-23Martin Stjernholm  if (!mappingp (overridden_tag_lookup)) overridden_tag_lookup = set_weak_flag (([]), 1);
2e0a6d2000-07-06Martin Stjernholm  Tag tag;
09b6c52001-01-24Martin Stjernholm  if (zero_type (tag = overridden_tag_lookup[overrider])) { string overrider_name = overrider->plugin_name ? overrider->plugin_name + "#" + overrider->name : overrider->name;
2e0a6d2000-07-06Martin Stjernholm  tag = overridden_tag_lookup[overrider] =
24f9e82000-08-05Martin Stjernholm  overrider->flags & FLAG_PROC_INSTR ?
09b6c52001-01-24Martin Stjernholm  find_overridden_proc_instr (overrider, overrider_name) : find_overridden_tag (overrider, overrider_name); }
51f88b2000-06-23Martin Stjernholm  return tag;
2bd21a2000-01-05Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm  local array(Tag) get_overridden_tags (string name, void|int proc_instr)
56ffee2000-01-12Martin Stjernholm  //! Returns all tag definitions for the given name, i.e. including //! the overridden ones. A tag to the left overrides one to the
4293072001-05-08Martin Stjernholm  //! right. If @[proc_instr] is nonzero the set of PI tags is //! searched, else the set of normal element tags.
56ffee2000-01-12Martin Stjernholm  {
24f9e82000-08-05Martin Stjernholm  if (object(Tag) def = get_local_tag (name, proc_instr)) return ({def}) + imported->get_overridden_tags (name, proc_instr) * ({}); else return imported->get_overridden_tags (name, proc_instr) * ({});
2e0a6d2000-07-06Martin Stjernholm  } void add_string_entities (mapping(string:string) entities)
fe628d2001-03-01Martin Stjernholm  //! Adds a set of entity replacements that are used foremost by the
4293072001-05-08Martin Stjernholm  //! @[RXML.PXml] parser to decode simple entities like @tt{&amp;@}. //! The indices are the entity names without @tt{&@} and @tt{;@}.
2e0a6d2000-07-06Martin Stjernholm  { if (string_entities) string_entities |= entities; else string_entities = entities + ([]); changed(); } void clear_string_entities() //! { string_entities = 0; changed(); }
689dc62000-08-31Martin Stjernholm  local mapping(string:string) get_string_entities()
2e0a6d2000-07-06Martin Stjernholm  //! Returns the set of entity replacements, including those from //! imported tag sets. { if (string_entities) return `+(@imported->get_string_entities(), string_entities); else return `+(@imported->get_string_entities(), ([]));
56ffee2000-01-12Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm  local mapping(string:Tag) get_plugins (string name, void|int proc_instr)
4293072001-05-08Martin Stjernholm  //! 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.
c159ec2000-02-04Martin Stjernholm  { mapping(string:Tag) res;
24f9e82000-08-05Martin Stjernholm  if (proc_instr) { if (!pi_plugins) pi_plugins = ([]); if ((res = pi_plugins[name])) return res; low_get_pi_plugins (name + "#", res = ([])); return pi_plugins[name] = res; } else { if (!plugins) plugins = ([]); if ((res = plugins[name])) return res; low_get_plugins (name + "#", res = ([])); return plugins[name] = res; }
c159ec2000-02-04Martin Stjernholm  }
936f632001-07-16Martin Stjernholm  local string get_hash() //! Returns a hash string built from all the tags and imported tag //! sets. It's suitable for use in persistent data to detect whether //! the tag set has changed in any way that would cause different //! tags to be parsed, or if they would be bound to different tag //! definitions. //! //! @note //! In the non-persistent case it's much more efficient to use //! @[generation] to track changes in the tag set. { if (!hash) hash = Crypto.md5()->update (encode_value_canonic (get_hash_data()))->digest(); return hash; }
2aa1dc2001-08-11Martin Stjernholm  local void add_tag_set_dependency (TagSet tset) //! Makes this tag set depend on @[tset], so that any change in it //! invalidates this tag set and affects the hash returned by //! @[get_hash]. //! //! This kind of dependency normally exists on the imported tag //! sets, but this function lets you add a dependency without //! getting the tags imported. It's typically useful to get proper //! dependencies on tag sets that contain local or runtime tags. { dep_tag_sets[tset] = 1; tset->do_notify (changed); changed(); } local void remove_tag_set_dependency (TagSet tset) //! Removes a dependency added by @[add_tag_set_dependency]. { dep_tag_sets[tset] = 0; tset->dont_notify (changed); }
689dc62000-08-31Martin Stjernholm  local int has_effective_tags (TagSet tset)
87fb7e2000-03-25Martin Stjernholm  //! This one deserves some explanation. { return tset == top_tag_set && !got_local_tags; }
689dc62000-08-31Martin Stjernholm  local mixed `->= (string var, mixed val)
ed81751999-12-11Martin Stjernholm  { switch (var) {
deecca2001-06-28Martin Stjernholm  case "name":
7e0fda2001-07-12Martin Stjernholm  if (name) all_tagsets[name] = generation;
c1ac6c2001-07-02Martin Stjernholm  set_name (val);
deecca2001-06-28Martin Stjernholm  break;
ed81751999-12-11Martin Stjernholm  case "imported":
49897a2000-02-13Martin Stjernholm  if (!val) return val; // Pike can call us with 0 as part of an optimization. filter (imported, "dont_notify", changed);
ed81751999-12-11Martin Stjernholm  imported = [array(TagSet)] val; imported->do_notify (changed);
87fb7e2000-03-25Martin Stjernholm  top_tag_set = sizeof (imported) && imported[0];
ed81751999-12-11Martin Stjernholm  break; default: ::`->= (var, val); } changed(); return val; }
689dc62000-08-31Martin Stjernholm  local mixed `[]= (string var, mixed val) {return `->= (var, val);}
ed81751999-12-11Martin Stjernholm 
be73272001-06-18Martin Stjernholm  final Context new_context (void|RequestID id) //! Creates and initializes a new context for use with a parser or //! @[RXML.PCode] object based on this tag set. @[id] is put into //! the context if given. Normally you'd rather use @[get_parser] or //! @[RXML.PCode.new_context] instead of this function.
ed81751999-12-11Martin Stjernholm  {
660cb52000-01-12Martin Stjernholm  Context ctx = Context (this_object(), id);
be73272001-06-18Martin Stjernholm  call_prepare_funs (ctx); return ctx;
ed81751999-12-11Martin Stjernholm  }
be73272001-06-18Martin Stjernholm  final Parser get_parser (Type top_level_type, void|RequestID id, void|int make_p_code) //! Creates a new context for parsing content of the specified type, //! and returns the parser object for it. @[id] is put into the //! context. The parser will collect an @[RXML.PCode] object if //! @[make_p_code] is nonzero. { return new_context (id)->new_parser (top_level_type, make_p_code); } final Parser `() (Type top_level_type, void|RequestID id, void|int make_p_code) //! For compatibility. Use @[get_parser] instead.
bce4d22001-06-19Martin Stjernholm  { return get_parser (top_level_type, id, make_p_code); }
be73272001-06-18Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  void changed() //! Should be called whenever something is changed. Done //! automatically most of the time, however. { generation++;
660cb52000-01-12Martin Stjernholm  prepare_funs = 0;
51f88b2000-06-23Martin Stjernholm  overridden_tag_lookup = 0;
24f9e82000-08-05Martin Stjernholm  plugins = pi_plugins = 0;
936f632001-07-16Martin Stjernholm  hash = 0;
ed81751999-12-11Martin Stjernholm  (notify_funcs -= ({0}))(); set_weak_flag (notify_funcs, 1);
24f9e82000-08-05Martin Stjernholm  got_local_tags = sizeof (tags) || (proc_instrs && sizeof (proc_instrs));
ed81751999-12-11Martin Stjernholm  }
3083572001-04-24Martin Nilsson  function(Backtrace,Type:string) handle_run_error = lambda (Backtrace err, Type type) //! Formats the run error backtrace.
f5bd9e2001-04-25Martin Nilsson  // This wrapper function however only search for a // "real" handle_run_error function.
3083572001-04-24Martin Nilsson  {
f5bd9e2001-04-25Martin Nilsson  string result; foreach(imported, TagSet tag_set) { result = tag_set->handle_run_error(err, type); if(result) return result; } return 0;
3083572001-04-24Martin Nilsson  }; function(Backtrace,Type:string) handle_parse_error = lambda (Backtrace err, Type type) //! Formats the parse error backtrace.
f5bd9e2001-04-25Martin Nilsson  // This wrapper function however only search for a // "real" handle_parse_error function.
3083572001-04-24Martin Nilsson  {
f5bd9e2001-04-25Martin Nilsson  string result; foreach(imported, TagSet tag_set) { result = tag_set->handle_parse_error(err, type); if(result) return result; } return 0;
3083572001-04-24Martin Nilsson  };
5c7a6b2001-06-25Martin Stjernholm  // Internals:
ed81751999-12-11Martin Stjernholm  void do_notify (function(:void) func) { notify_funcs |= ({func}); set_weak_flag (notify_funcs, 1); } void dont_notify (function(:void) func) { notify_funcs -= ({func}); set_weak_flag (notify_funcs, 1); }
fae5212001-06-28Martin Stjernholm  static void destroy()
ed81751999-12-11Martin Stjernholm  { catch (changed());
7e0fda2001-07-12Martin Stjernholm  if (name) all_tagsets[name] = generation;
ed81751999-12-11Martin Stjernholm  }
c1ac6c2001-07-02Martin Stjernholm  static void set_name (string new_name) { if (new_name) {
10bd2c2001-07-12Martin Stjernholm  object(TagSet)|int old_tag_set = all_tagsets[new_name]; if (objectp (old_tag_set)) { // It'd be nice if we could warn about duplicate tag sets with // the same name here, but unfortunately that doesn't work // well enough: Local tag sets from old module instances might // still be around with references from cached frames in stale // p-code. We can remove the name in the old tag set anyway, // so that it doesn't work to dump references to it. old_tag_set->name = 0; old_tag_set = old_tag_set->generation;
c1ac6c2001-07-02Martin Stjernholm  }
10bd2c2001-07-12Martin Stjernholm  if (generation <= old_tag_set) generation = old_tag_set; all_tagsets[name = new_name] = this_object();
c1ac6c2001-07-02Martin Stjernholm  }
10bd2c2001-07-12Martin Stjernholm  else name = 0;
c1ac6c2001-07-02Martin Stjernholm  }
24f9e82000-08-05Martin Stjernholm  static mapping(string:Tag) tags = ([]), proc_instrs; // Static since we want to track changes in these.
2e0a6d2000-07-06Martin Stjernholm  static mapping(string:string) string_entities; // Used by e.g. PXml to hold normal entities that should be replaced // during parsing.
ed81751999-12-11Martin Stjernholm 
87fb7e2000-03-25Martin Stjernholm  static TagSet top_tag_set; // The imported tag set with the highest priority. static int got_local_tags;
24f9e82000-08-05Martin Stjernholm  // Nonzero if there are local element tags or PI tags.
87fb7e2000-03-25Martin Stjernholm 
c159ec2000-02-04Martin Stjernholm  static array(function(:void)) notify_funcs = ({});
ed81751999-12-11Martin Stjernholm  // Weak (when nonempty).
2bd21a2000-01-05Martin Stjernholm 
c159ec2000-02-04Martin Stjernholm  static array(function(Context:void)) prepare_funs;
660cb52000-01-12Martin Stjernholm 
2aa1dc2001-08-11Martin Stjernholm  static multiset(TagSet) dep_tag_sets = set_weak_flag ((<>), 1);
c159ec2000-02-04Martin Stjernholm  /*static*/ array(function(Context:void)) get_prepare_funs()
660cb52000-01-12Martin Stjernholm  { if (prepare_funs) return prepare_funs; array(function(Context:void)) funs = ({}); for (int i = sizeof (imported) - 1; i >= 0; i--) funs += imported[i]->get_prepare_funs(); if (prepare_context) funs += ({prepare_context}); // We don't cache in prepare_funs; do that only at the top level. return funs; }
be73272001-06-18Martin Stjernholm  final void call_prepare_funs (Context ctx) { if (!prepare_funs) prepare_funs = get_prepare_funs(); (prepare_funs -= ({0})) (ctx); } static array(function(Context:void)) eval_finish_funs; /*static*/ array(function(Context:void)) get_eval_finish_funs() { if (eval_finish_funs) return eval_finish_funs; array(function(Context:void)) funs = ({}); for (int i = sizeof (imported) - 1; i >= 0; i--) funs += imported[i]->get_eval_finish_funs(); if (eval_finish) funs += ({eval_finish}); // We don't cache in eval_finish_funs; do that only at the top level. return funs; } void call_eval_finish_funs (Context ctx) { if (!eval_finish_funs) eval_finish_funs = get_eval_finish_funs(); (eval_finish_funs -= ({0})) (ctx); }
2e0a6d2000-07-06Martin Stjernholm  static mapping(Tag:Tag) overridden_tag_lookup;
51f88b2000-06-23Martin Stjernholm 
09b6c52001-01-24Martin Stjernholm  /*static*/ Tag find_overridden_tag (Tag overrider, string overrider_name)
51f88b2000-06-23Martin Stjernholm  {
09b6c52001-01-24Martin Stjernholm  if (tags[overrider_name] == overrider) {
51f88b2000-06-23Martin Stjernholm  foreach (imported, TagSet tag_set)
09b6c52001-01-24Martin Stjernholm  if (object(Tag) overrider = tag_set->get_tag (overrider_name))
2e0a6d2000-07-06Martin Stjernholm  return overrider;
51f88b2000-06-23Martin Stjernholm  } else { int found = 0; foreach (imported, TagSet tag_set)
09b6c52001-01-24Martin Stjernholm  if (object(Tag) subtag = tag_set->get_tag (overrider_name))
51f88b2000-06-23Martin Stjernholm  if (found) return subtag;
2e0a6d2000-07-06Martin Stjernholm  else if (subtag == overrider)
09b6c52001-01-24Martin Stjernholm  if ((subtag = tag_set->find_overridden_tag ( overrider, overrider_name)))
51f88b2000-06-23Martin Stjernholm  return subtag; else found = 1; } return 0; }
09b6c52001-01-24Martin Stjernholm  /*static*/ Tag find_overridden_proc_instr (Tag overrider, string overrider_name)
24f9e82000-08-05Martin Stjernholm  {
09b6c52001-01-24Martin Stjernholm  if (proc_instrs && proc_instrs[overrider_name] == overrider) {
24f9e82000-08-05Martin Stjernholm  foreach (imported, TagSet tag_set)
4293072001-05-08Martin Stjernholm  if (object(Tag) overrider = tag_set->get_tag (overrider_name, 1))
24f9e82000-08-05Martin Stjernholm  return overrider; } else { int found = 0; foreach (imported, TagSet tag_set)
4293072001-05-08Martin Stjernholm  if (object(Tag) subtag = tag_set->get_tag (overrider_name, 1))
24f9e82000-08-05Martin Stjernholm  if (found) return subtag; else if (subtag == overrider)
09b6c52001-01-24Martin Stjernholm  if ((subtag = tag_set->find_overridden_proc_instr ( overrider, overrider_name)))
24f9e82000-08-05Martin Stjernholm  return subtag; else found = 1; } return 0; } static mapping(string:mapping(string:Tag)) plugins, pi_plugins;
c159ec2000-02-04Martin Stjernholm  /*static*/ void low_get_plugins (string prefix, mapping(string:Tag) res) { for (int i = sizeof (imported) - 1; i >= 0; i--) imported[i]->low_get_plugins (prefix, res); foreach (indices (tags), string name) if (name[..sizeof (prefix) - 1] == prefix) { Tag tag = tags[name]; if (tag->plugin_name) res[[string] tag->plugin_name] = tag; } // We don't cache in plugins; do that only at the top level. }
24f9e82000-08-05Martin Stjernholm  /*static*/ void low_get_pi_plugins (string prefix, mapping(string:Tag) res) { for (int i = sizeof (imported) - 1; i >= 0; i--) imported[i]->low_get_pi_plugins (prefix, res); if (proc_instrs) foreach (indices (proc_instrs), string name) if (name[..sizeof (prefix) - 1] == prefix) { Tag tag = proc_instrs[name]; if (tag->plugin_name) res[[string] tag->plugin_name] = tag; } // We don't cache in pi_plugins; do that only at the top level. }
936f632001-07-16Martin Stjernholm  static string hash; /*static*/ array get_hash_data() { return ({ prefix, prefix_req, sort (indices (tags)), proc_instrs && sort (indices (proc_instrs)), string_entities,
2aa1dc2001-08-11Martin Stjernholm  }) + imported->get_hash_data() + ({0}) + indices (dep_tag_sets)->get_hash_data();
936f632001-07-16Martin Stjernholm  }
2bd21a2000-01-05Martin Stjernholm  string _sprintf() {
cf042f2001-07-21Martin Stjernholm  return "RXML.TagSet(" + id_number + ")" + OBJ_COUNT;
2bd21a2000-01-05Martin Stjernholm  }
ec027c2000-03-16Martin Stjernholm 
112b012001-08-10Martin Nilsson  //! @ignore
f02d002000-03-18Martin Stjernholm  MARK_OBJECT_ONLY;
112b012001-08-10Martin Nilsson  //! @endignore
ed81751999-12-11Martin Stjernholm }
1b2b752000-01-07Martin Stjernholm TagSet empty_tag_set;
ed81751999-12-11Martin Stjernholm //! The empty tag set.
02a6562001-07-09Martin Stjernholm TagSet shared_tag_set (string name, void|array(Tag) tags) //! If a tag set with the given name exists, it's returned. Otherwise //! a new tag set is created with that name. @[tags] is passed along //! to its @[RXML.TagSet.create] function in that case. {
7e0fda2001-07-12Martin Stjernholm  if (TagSet tag_set = all_tagsets[name]) if (objectp (tag_set)) return tag_set;
02a6562001-07-09Martin Stjernholm  return TagSet (name, tags); }
ed81751999-12-11Martin Stjernholm 
af06d52000-01-12Martin Stjernholm class Value //! Interface for objects used as variable values that are evaluated //! when referenced. {
a21fcb2000-02-08Martin Nilsson  mixed rxml_var_eval (Context ctx, string var, string scope_name, void|Type type)
2b5f472001-02-11Martin Stjernholm  //! 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
4293072001-05-08Martin Stjernholm  //! @tt{"scope.index1.index2..."@} when this object was encountered
0c9e5b2001-03-13Martin Stjernholm  //! through subindexing. Either @[RXML.nil] or the undefined value //! may be returned if the variable doesn't have a value.
710b482000-02-07Martin Stjernholm  //!
2b5f472001-02-11Martin Stjernholm  //! If the @[type] argument is given, it's the type the returned //! value should have. If the value can't be converted to that type, //! an RXML error should be thrown. If you don't want to do any //! special handling of this, it's enough to call
151fa92001-04-19Johan Sundström  //! @code{@[type]->encode(value)@}, since the encode functions does
2b5f472001-02-11Martin Stjernholm  //! just that. //! //! Some design discussion follows to justify the last paragraph; //! there are no more interface rules below. //! //! It may seem like forcing a lot of overhead upon the //! implementations having to call encode functions, but that's //! really not the case. In the case when a type check and //! conversion is wanted, i.e. when @[type] isn't zero, that work //! have to be done somewhere anyway, so letting the producer of the //! value do it instead of the caller both improves the chances for //! doing optimizations and gives more power to the producer. //! //! By using knowledge about the actual value, the producer can in //! many cases avoid the call to the encode function. A typical case
ec860d2001-03-13Martin Stjernholm  //! is when the value is known to be an arbitrary literal string //! (not zero), which is preferably optimized like this:
2b5f472001-02-11Martin Stjernholm  //!
112b012001-08-10Martin Nilsson  //! @code{
f55a012001-03-14Martin Stjernholm  //! return type && type != RXML.t_text ? //! type->encode (my_string, RXML.t_text) : my_string;
112b012001-08-10Martin Nilsson  //! @}
2b5f472001-02-11Martin Stjernholm  //! //! Also, by letting the producer know the type context of the value //! and handle the type conversion, it's possible for the producer //! to adapt the value according to the context it'll be used in, //! e.g. to return a powerful object if no type conversion is //! wanted, a simple text representation of it when the type is
f55a012001-03-14Martin Stjernholm  //! @[RXML.t_text], and a more nicely formatted representation when
fe628d2001-03-01Martin Stjernholm  //! it's @[RXML.t_html].
2b5f472001-02-11Martin Stjernholm  //!
151fa92001-04-19Johan Sundström  //! @note //! The @[type] argument being @tt{void|Type@} means that the caller //! is free to leave out that argument, not that the function
2b5f472001-02-11Martin Stjernholm  //! implementor is free to ignore it.
a21fcb2000-02-08Martin Nilsson  { mixed val = rxml_const_eval (ctx, var, scope_name, type); ctx->set_var(var, val, scope_name); return val; }
9d80e82000-02-11Martin Stjernholm  mixed rxml_const_eval (Context ctx, string var, string scope_name, void|Type type);
2b5f472001-02-11Martin Stjernholm  //! If the variable value is the same throughout the life of the //! context, this method should be used instead of @[rxml_var_eval].
26ff092000-01-21Martin Stjernholm  string _sprintf() {return "RXML.Value";}
af06d52000-01-12Martin Stjernholm } class Scope
2b5f472001-02-11Martin Stjernholm //! Interface for objects that emulate a scope mapping.
1cfbeb2000-11-06Martin Stjernholm //!
151fa92001-04-19Johan Sundström //! @note //! The @tt{scope_name@} argument to the functions can be on the form
4293072001-05-08Martin Stjernholm //! @tt{"scope.index1.index2..."@} when this object was encountered
151fa92001-04-19Johan Sundström //! through subindexing.
af06d52000-01-12Martin Stjernholm {
2b5f472001-02-11Martin Stjernholm  mixed `[] (string var, void|Context ctx, void|string scope_name, void|Type type) //! Called to get the value of a variable in the scope. @[var] is //! the name of it, @[ctx] and @[scope_name] are set to where this
0c9e5b2001-03-13Martin Stjernholm  //! @[Scope] object was found. Either @[RXML.nil] or the undefined //! value may be returned if the variable doesn't exist in the //! scope.
2b5f472001-02-11Martin Stjernholm  //! //! If the @[type] argument is given, it's the type the returned //! value should have, unless it's an object which implements //! @[Value.rxml_var_eval]. If the value can't be converted to that //! type, an RXML error should be thrown. If you don't want to do //! any special handling of this, it's enough to call //! @tt{@[type]->encode(value)@}, since the encode functions does //! just that. See @[Value.rxml_var_eval] for more discussion about //! this. //!
151fa92001-04-19Johan Sundström  //! @note //! The @[type] argument being @tt{void|Type@} means that the caller //! is free to leave out that argument, not that the function
2b5f472001-02-11Martin Stjernholm  //! implementor is free to ignore it.
5ed0132000-02-13Martin Stjernholm  {parse_error ("Cannot query variable" + _in_the_scope (scope_name) + ".\n");}
01e43b2000-01-14Martin Stjernholm  mixed `[]= (string var, mixed val, void|Context ctx, void|string scope_name)
2b5f472001-02-11Martin Stjernholm  //! Called to set the value of a variable in the scope. @[var] is //! the name of it, @[ctx] and @[scope_name] are set to where this //! @[Scope] object was found. //! //! An RXML error may be thrown if the value is not acceptable for //! the variable. It's undefined what happens if a variable is set
0c9e5b2001-03-13Martin Stjernholm  //! to @[RXML.nil]; it should be avoided.
5ed0132000-02-13Martin Stjernholm  {parse_error ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");}
01e43b2000-01-14Martin Stjernholm  array(string) _indices (void|Context ctx, void|string scope_name)
2b5f472001-02-11Martin Stjernholm  //! Called to get a list of all defined variables in the scope. //! @[ctx] and @[scope_name] are set to where this @[Scope] object //! was found. //! //! There's no guarantee that the returned variable names produce a
0c9e5b2001-03-13Martin Stjernholm  //! value (i.e. neither @[RXML.nil] nor the undefined value) when
2b5f472001-02-11Martin Stjernholm  //! indexed.
5ed0132000-02-13Martin Stjernholm  {parse_error ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");}
01e43b2000-01-14Martin Stjernholm 
2b5f472001-02-11Martin Stjernholm  void _m_delete (string var, void|Context ctx, void|string scope_name) //! Called to delete a variable in the scope. @[var] is the name of //! it, @[ctx] and @[scope_name] are set to where this @[Scope] //! object was found. { if (m_delete != local::m_delete) m_delete (var, ctx, scope_name); // For compatibility with 2.1. else parse_error ("Cannot delete variable" + _in_the_scope (scope_name) + ".\n"); }
01e43b2000-01-14Martin Stjernholm  void m_delete (string var, void|Context ctx, void|string scope_name)
2b5f472001-02-11Martin Stjernholm  // For compatibility with 2.1. {_m_delete (var, ctx, scope_name);}
01e43b2000-01-14Martin Stjernholm  private string _in_the_scope (string scope_name) { if (scope_name)
3e59342000-01-18Martin Stjernholm  if (scope_name != "_") return " in the scope " + scope_name;
01e43b2000-01-14Martin Stjernholm  else return " in the current scope"; else return ""; }
26ff092000-01-21Martin Stjernholm  string _sprintf() {return "RXML.Scope";}
af06d52000-01-12Martin Stjernholm }
ed81751999-12-11Martin Stjernholm class Context //! A parser context. This contains the current variable bindings and //! so on. The current context can always be retrieved with
4293072001-05-08Martin Stjernholm //! @[RXML.get_context].
ed81751999-12-11Martin Stjernholm //!
151fa92001-04-19Johan Sundström //! @note //! Don't store pointers to this object since that will likely
ed81751999-12-11Martin Stjernholm //! introduce circular references. It can be retrieved easily through
4293072001-05-08Martin Stjernholm //! @[RXML.get_context].
ed81751999-12-11Martin Stjernholm { Frame frame; //! The currently evaluating frame.
ffa6932000-02-20Martin Stjernholm  int frame_depth;
be73272001-06-18Martin Stjernholm  //! Current evaluation recursion depth. This might be more than the //! number of frames in the @[frame] linked chain.
ffa6932000-02-20Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  constant max_frame_depth = 100;
be73272001-06-18Martin Stjernholm  //! Maximum allowed evaluation recursion depth.
ffa6932000-02-20Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  RequestID id; //!
be73272001-06-18Martin Stjernholm  mapping(mixed:mixed) misc = ([]); //! Various context info. This is typically the same mapping as //! @tt{id->misc->defines@}. To avoid accidental namespace conflicts //! in this mapping, it's suggested that the module/tag program or //! object is used as index in it.
ed81751999-12-11Martin Stjernholm  int type_check;
02a6562001-07-09Martin Stjernholm  // Whether to do type checking. FIXME: Not fully implemented.
ed81751999-12-11Martin Stjernholm 
db04172000-01-14Martin Stjernholm  int error_count;
deecca2001-06-28Martin Stjernholm  //! Number of RXML errors that has occurred. If this is nonzero, the //! result of the evaluation shouldn't be trusted, but it might be //! wise to return it to the user anyway, as it can contain error //! reports (see @[Parser.recover_errors] and @[FLAG_DONT_RECOVER] //! for further details about error reporting).
01e43b2000-01-14Martin Stjernholm 
1b2b752000-01-07Martin Stjernholm  TagSet tag_set;
ed81751999-12-11Martin Stjernholm  //! The current tag set that will be inherited by subparsers.
c8021c2000-02-15Martin Nilsson #ifdef OLD_RXML_COMPAT int compatible_scope = 0;
4293072001-05-08Martin Stjernholm  //! 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]). //!
151fa92001-04-19Johan Sundström  //! @note
4293072001-05-08Martin Stjernholm  //! This is only present when the @tt{OLD_RXML_COMPAT@} define is //! set. #endif
9686722001-07-25Martin Stjernholm  void state_update() //! Should be called whenever the persistent state changes. For tag //! implementors that means whenever the value that //! @[RXML.Frame.save] would return changes. { state_updated++; }
8abef52001-02-11Martin Stjernholm  array(string|int) parse_user_var (string var, void|string|int scope_name)
1cfbeb2000-11-06Martin Stjernholm  //! Parses the var string for scope and/or subindexes according to
4293072001-05-08Martin Stjernholm  //! 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
6dfc812001-07-20Martin Stjernholm  //! whether @[var] actually was split or not).
1cfbeb2000-11-06Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! @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.
9390fe2000-01-23Martin Nilsson  {
c8021c2000-02-15Martin Nilsson #ifdef OLD_RXML_COMPAT
2cab1b2001-03-05Martin Nilsson  if (compatible_scope && !intp(scope_name))
1cfbeb2000-11-06Martin Stjernholm  return ({scope_name || "form", var});
c8021c2000-02-15Martin Nilsson #endif
1cfbeb2000-11-06Martin Stjernholm 
1766fe2001-02-11Martin Nilsson  array(string|int) splitted;
0aa54b2001-02-11Martin Stjernholm  if(has_value(var, "..")) {
6ec8cc2001-02-11Martin Stjernholm  // The \0 stuff is really here for a reason: The _only_ special // character is '.'.
0aa54b2001-02-11Martin Stjernholm  string coded = replace (var, "\0", "\0\0"); if (coded != var) splitted = map (replace (coded, "..", "\0p") / ".", replace, ({"\0p", "\0\0"}), ({".", "\0"})); else splitted = map (replace (var, "..", "\0") / ".", replace, "\0", "."); }
1766fe2001-02-11Martin Nilsson  else splitted = var / ".";
8abef52001-02-11Martin Stjernholm  if (stringp (scope_name))
1cfbeb2000-11-06Martin Stjernholm  splitted = ({scope_name}) + splitted;
8abef52001-02-11Martin Stjernholm  else if (sizeof (splitted) == 1) splitted = ({scope_name || "_"}) + splitted;
1cfbeb2000-11-06Martin Stjernholm  for (int i = 2; i < sizeof (splitted); i++) if (sscanf (splitted[i], "%d%*c", int d) == 1) splitted[i] = d; return splitted;
9390fe2000-01-23Martin Nilsson  }
1cfbeb2000-11-06Martin Stjernholm  local mixed get_var (string|array(string|int) var, void|string scope_name, void|Type want_type)
4293072001-05-08Martin Stjernholm  //! 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]).
8778dd2000-02-05Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! If @[var] is an array, it's used to successively index the value //! to get subvalues (see @[rxml_index] for details).
1cfbeb2000-11-06Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! 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.
1cfbeb2000-11-06Martin Stjernholm  { #ifdef MODULE_DEBUG if (arrayp (var) ? !sizeof (var) : !stringp (var))
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Invalid variable specifier.\n");
0ddf4e2000-09-15Martin Nilsson #endif
1cfbeb2000-11-06Martin Stjernholm  if (!scope_name) scope_name = "_"; if (SCOPE_TYPE vars = scopes[scope_name]) return rxml_index (vars, var, scope_name, this_object(), want_type); else if (scope_name == "_") parse_error ("No current scope.\n"); else parse_error ("Unknown scope %O.\n", scope_name);
ed81751999-12-11Martin Stjernholm  }
8778dd2000-02-05Martin Stjernholm  mixed user_get_var (string var, void|string scope_name, void|Type want_type)
4293072001-05-08Martin Stjernholm  //! 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).
a08cd72001-06-09Martin Stjernholm  //! //! @note //! This is intended for situations where you get a variable //! reference on the dot form in e.g. user input. In other cases, //! when the scope and variable is known, it's more efficient to use //! @[get_var].
9390fe2000-01-23Martin Nilsson  { if(!var || !sizeof(var)) return ([])[0];
1cfbeb2000-11-06Martin Stjernholm  array(string|int) splitted = parse_user_var (var, scope_name); return get_var (splitted[1..], splitted[0], want_type);
9390fe2000-01-23Martin Nilsson  }
1cfbeb2000-11-06Martin Stjernholm  local mixed set_var (string|array(string|int) var, mixed val, void|string scope_name)
ed81751999-12-11Martin Stjernholm  //! Sets the value of a variable in the specified scope, or the
4293072001-05-08Martin Stjernholm  //! current scope if none is given. Returns @[val].
1cfbeb2000-11-06Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! If @[var] is an array, it's used to successively index the value //! to get subvalues (see @[rxml_index] for details).
ed81751999-12-11Martin Stjernholm  {
1cfbeb2000-11-06Martin Stjernholm #ifdef MODULE_DEBUG if (arrayp (var) ? !sizeof (var) : !stringp (var))
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Invalid variable specifier.\n");
1cfbeb2000-11-06Martin Stjernholm #endif if (!scope_name) scope_name = "_"; if (SCOPE_TYPE vars = scopes[scope_name]) { string|int index; if (arrayp (var)) if (sizeof (var) > 1) { index = var[-1];
6dfc812001-07-20Martin Stjernholm  array(string|int) path = var[..sizeof (var) - 1]; vars = rxml_index (vars, path, scope_name, this_object()); scope_name += "." + (array(string)) path * "."; if (mapping(string:mixed) var_chg = misc->variable_changes)
a835e82001-07-25Martin Stjernholm  var_chg[encode_value_canonic (({scope_name}) + var)] = val;
1cfbeb2000-11-06Martin Stjernholm  }
6dfc812001-07-20Martin Stjernholm  else { index = var[0]; if (mapping(string:mixed) var_chg = misc->variable_changes) var_chg[encode_value_canonic (({scope_name, index}))] = val; } else { index = var; if (mapping(string:mixed) var_chg = misc->variable_changes) var_chg[encode_value_canonic (({scope_name, index}))] = val; }
1cfbeb2000-11-06Martin Stjernholm  if (objectp (vars) && vars->`[]=) return ([object(Scope)] vars)->`[]= (index, val, this_object(), scope_name); else if (mappingp (vars) || multisetp (vars)) return vars[index] = val; else if (arrayp (vars)) if (intp (index) && index) if ((index < 0 ? -index : index) > sizeof (vars)) parse_error( "Index %d out of range for array of size %d in %s.\n", index, sizeof (val), scope_name ); else if (index < 0) return vars[index] = val; else return vars[index - 1] = val; else
a835e82001-07-25Martin Stjernholm  parse_error( "Cannot index the array in %s with %s.\n", scope_name, format_short (index) );
01e43b2000-01-14Martin Stjernholm  else
a835e82001-07-25Martin Stjernholm  parse_error ("%s is %s which cannot be indexed with %s.\n", scope_name, format_short (vars), format_short (index));
1cfbeb2000-11-06Martin Stjernholm  } else if (scope_name == "_") parse_error ("No current scope.\n"); else parse_error ("Unknown scope %O.\n", scope_name);
ed81751999-12-11Martin Stjernholm  }
9390fe2000-01-23Martin Nilsson  mixed user_set_var (string var, mixed val, void|string scope_name)
4293072001-05-08Martin Stjernholm  //! 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).
a08cd72001-06-09Martin Stjernholm  //! //! @note //! This is intended for situations where you get a variable //! reference on the dot form in e.g. user input. In other cases, //! when the scope and variable is known, it's more efficient to use //! @[set_var].
9390fe2000-01-23Martin Nilsson  {
c8021c2000-02-15Martin Nilsson  if(!var || !sizeof(var)) parse_error ("No variable specified.\n");
1cfbeb2000-11-06Martin Stjernholm  array(string|int) splitted = parse_user_var (var, scope_name); return set_var(splitted[1..], val, splitted[0]);
9390fe2000-01-23Martin Nilsson  }
1cfbeb2000-11-06Martin Stjernholm  local void delete_var (string|array(string|int) var, void|string scope_name)
ed81751999-12-11Martin Stjernholm  //! Removes a variable in the specified scope, or the current scope //! if none is given.
1cfbeb2000-11-06Martin Stjernholm  //!
4293072001-05-08Martin Stjernholm  //! If @[var] is an array, it's used to successively index the value //! to get subvalues (see @[rxml_index] for details).
ed81751999-12-11Martin Stjernholm  {
1cfbeb2000-11-06Martin Stjernholm #ifdef MODULE_DEBUG if (arrayp (var) ? !sizeof (var) : !stringp (var))
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Invalid variable specifier.\n");
1cfbeb2000-11-06Martin Stjernholm #endif if (!scope_name) scope_name = "_"; if (SCOPE_TYPE vars = scopes[scope_name]) { if (arrayp (var)) if (sizeof (var) > 1) {
a835e82001-07-25Martin Stjernholm  array(string|int) path = var[..sizeof (var) - 1]; vars = rxml_index (vars, path, scope_name, this_object()); scope_name += "." + (array(string)) path * "."; if (mapping(string:mixed) var_chg = misc->variable_changes) var_chg[encode_value_canonic (({scope_name}) + var)] = nil; var = var[-1];
1cfbeb2000-11-06Martin Stjernholm  }
a835e82001-07-25Martin Stjernholm  else { var = var[0]; if (mapping(string:mixed) var_chg = misc->variable_changes) var_chg[encode_value_canonic (({scope_name, var}))] = nil; } else { if (mapping(string:mixed) var_chg = misc->variable_changes) var_chg[encode_value_canonic (({scope_name, var}))] = nil; }
1cfbeb2000-11-06Martin Stjernholm 
2b5f472001-02-11Martin Stjernholm  if (objectp (vars) && vars->_m_delete) ([object(Scope)] vars)->_m_delete (var, this_object(), scope_name);
1cfbeb2000-11-06Martin Stjernholm  else if (mappingp (vars))
01e43b2000-01-14Martin Stjernholm  m_delete ([mapping(string:mixed)] vars, var);
1cfbeb2000-11-06Martin Stjernholm  else if (multisetp (vars)) vars[var] = 0; else
a835e82001-07-25Martin Stjernholm  parse_error ("Cannot remove the index %s from the %t in %s.\n", format_short (var), vars, scope_name);
1cfbeb2000-11-06Martin Stjernholm  } else if (scope_name == "_") parse_error ("No current scope.\n");
9290ad2000-02-20Martin Stjernholm  else parse_error ("Unknown scope %O.\n", scope_name);
ed81751999-12-11Martin Stjernholm  }
291ade2000-01-25Martin Stjernholm  void user_delete_var (string var, void|string scope_name)
4293072001-05-08Martin Stjernholm  //! 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).
a08cd72001-06-09Martin Stjernholm  //! //! @note //! This is intended for situations where you get a variable //! reference on the dot form in e.g. user input. In other cases, //! when the scope and variable is known, it's more efficient to use //! @[delete_var].
9390fe2000-01-23Martin Nilsson  {
291ade2000-01-25Martin Stjernholm  if(!var || !sizeof(var)) return;
1cfbeb2000-11-06Martin Stjernholm  array(string|int) splitted = parse_user_var (var, scope_name); delete_var(splitted[1..], splitted[0]);
9390fe2000-01-23Martin Nilsson  }
ed81751999-12-11Martin Stjernholm  array(string) list_var (void|string scope_name) //! Returns the names of all variables in the specified scope, or //! the current scope if none is given. {
3e59342000-01-18Martin Stjernholm  if (SCOPE_TYPE vars = scopes[scope_name || "_"])
01e43b2000-01-14Martin Stjernholm  if (objectp (vars))
3e59342000-01-18Martin Stjernholm  return ([object(Scope)] vars)->_indices (this_object(), scope_name || "_");
01e43b2000-01-14Martin Stjernholm  else return indices ([mapping(string:mixed)] vars);
9290ad2000-02-20Martin Stjernholm  else if ((<0, "_">)[scope_name]) parse_error ("No current scope.\n"); else parse_error ("Unknown scope %O.\n", scope_name);
ed81751999-12-11Martin Stjernholm  } array(string) list_scopes() //! Returns the names of all defined scopes. {
3e59342000-01-18Martin Stjernholm  return indices (scopes) - ({"_"});
ed81751999-12-11Martin Stjernholm  }
af06d52000-01-12Martin Stjernholm  int exist_scope (void|string scope_name) //! {
3e59342000-01-18Martin Stjernholm  return !!scopes[scope_name || "_"];
af06d52000-01-12Martin Stjernholm  } void add_scope (string scope_name, SCOPE_TYPE vars) //! Adds or replaces the specified scope at the global level. A
4293072001-05-08Martin Stjernholm  //! scope can be a mapping or an @[RXML.Scope] object. A global //! @tt{"_"@} scope may also be defined this way.
ed81751999-12-11Martin Stjernholm  { if (scopes[scope_name])
3e59342000-01-18Martin Stjernholm  if (scope_name == "_") {
291ade2000-01-25Martin Stjernholm  array(SCOPE_TYPE) hid; for (Frame f = frame; f; f = f->up) if (array(SCOPE_TYPE) h = hidden[f]) hid = h; if (hid) hid[0] = vars; else scopes["_"] = vars;
ed81751999-12-11Martin Stjernholm  } else { Frame outermost; for (Frame f = frame; f; f = f->up) if (f->scope_name == scope_name) outermost = f;
291ade2000-01-25Martin Stjernholm  if (outermost) hidden[outermost][1] = vars;
ed81751999-12-11Martin Stjernholm  else scopes[scope_name] = vars; } else scopes[scope_name] = vars;
a835e82001-07-25Martin Stjernholm  if (mapping(string:mixed) var_chg = misc->variable_changes) { foreach (indices (var_chg), string encoded_var) { array var = decode_value (encoded_var); if (var[0] == scope_name) m_delete (var_chg, encoded_var); } var_chg[encode_value_canonic (({scope_name}))] = mappingp (vars) ? vars + ([]) : vars; }
ed81751999-12-11Martin Stjernholm  }
a835e82001-07-25Martin Stjernholm  void extend_scope (string scope_name, SCOPE_TYPE vars)
291ade2000-01-25Martin Stjernholm  //! Adds or extends the specified scope at the global level.
74fa382001-07-25Martin Stjernholm  //! //! @note //! The contents of @[vars] is currently transferred over to the //! existing scope object, if there is any. That's usually not an //! issue if the scopes are mappings, but can be if @[vars] is an //! @[RXML.Scope] object, or the existing scope is such an object //! that doesn't handle assignments.
0273b22000-01-21Martin Nilsson  {
291ade2000-01-25Martin Stjernholm  if (scopes[scope_name]) { SCOPE_TYPE oldvars; if (scope_name == "_") { array(SCOPE_TYPE) hid; for (Frame f = frame; f; f = f->up) if (array(SCOPE_TYPE) h = hidden[f]) hid = h; if (hid) oldvars = hid[0]; else oldvars = scopes["_"]; } else { Frame outermost; for (Frame f = frame; f; f = f->up) if (f->scope_name == scope_name) outermost = f; if (outermost) oldvars = hidden[outermost][1]; else oldvars = scopes[scope_name]; } #ifdef DEBUG
d52f8f2001-05-19Martin Stjernholm  if (!oldvars) fatal_error ("I before e except after c.\n");
291ade2000-01-25Martin Stjernholm #endif foreach (indices(vars), string var) set_var(var, vars[var], scope_name);
f6020c2000-01-23Martin Nilsson  }
a835e82001-07-25Martin Stjernholm  else { scopes[scope_name] = vars; if (mapping(string:mixed) var_chg = misc->variable_changes) { foreach (indices (var_chg), string encoded_var) { array var = decode_value (encoded_var); if (var[0] == scope_name) m_delete (var_chg, encoded_var); } var_chg[encode_value_canonic (({scope_name}))] = mappingp (vars) ? vars + ([]) : vars; } }
0273b22000-01-21Martin Nilsson  }
ed81751999-12-11Martin Stjernholm  void remove_scope (string scope_name) //! Removes the named scope from the global level, if it exists. { #ifdef MODULE_DEBUG
8cb24d2000-02-13Martin Stjernholm  if (scope_name == "_") fatal_error ("Cannot remove current scope.\n");
ed81751999-12-11Martin Stjernholm #endif Frame outermost; for (Frame f = frame; f; f = f->up) if (f->scope_name == scope_name) outermost = f; if (outermost) m_delete (hidden, outermost); else m_delete (scopes, scope_name);
a835e82001-07-25Martin Stjernholm  if (mapping(string:mixed) var_chg = misc->variable_changes) { foreach (indices (var_chg), string encoded_var) { array var = decode_value (encoded_var); if (var[0] == scope_name) m_delete (var_chg, encoded_var); } var_chg[encode_value_canonic (({scope_name}))] = 0; }
ed81751999-12-11Martin Stjernholm  } string current_scope() //! Returns the name of the current scope, if it has any. {
3e59342000-01-18Martin Stjernholm  if (SCOPE_TYPE vars = scopes["_"]) {
d52f8f2001-05-19Martin Stjernholm  string scope_name = search (scopes, vars); do
3e59342000-01-18Martin Stjernholm  if (scope_name != "_") return scope_name;
d52f8f2001-05-19Martin Stjernholm  while ((scope_name = search (scopes, vars, scope_name)));
ed81751999-12-11Martin Stjernholm  } return 0; }
be73272001-06-18Martin Stjernholm  SCOPE_TYPE get_scope (string scope_name) //! Returns the scope mapping/object for the given scope. { return scopes[scope_name]; }
af06d52000-01-12Martin Stjernholm  void add_runtime_tag (Tag tag) //! Adds a tag that will exist from this point forward in the
0ebc9d2000-02-15Martin Stjernholm  //! current context only.
af06d52000-01-12Martin Stjernholm  {
24f9e82000-08-05Martin Stjernholm #ifdef MODULE_DEBUG if (tag->plugin_name)
6dfc812001-07-20Martin Stjernholm  fatal_error ("Cannot handle plugin tags added at runtime.\n");
24f9e82000-08-05Martin Stjernholm #endif
0ebc9d2000-02-15Martin Stjernholm  if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags();
24f9e82000-08-05Martin Stjernholm  new_runtime_tags->add_tag (tag);
af06d52000-01-12Martin Stjernholm  }
24f9e82000-08-05Martin Stjernholm  void remove_runtime_tag (string|Tag tag, void|int proc_instr)
4293072001-05-08Martin Stjernholm  //! 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.
af06d52000-01-12Martin Stjernholm  {
0ebc9d2000-02-15Martin Stjernholm  if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags();
87fb7e2000-03-25Martin Stjernholm  if (objectp (tag)) tag = tag->name;
24f9e82000-08-05Martin Stjernholm  new_runtime_tags->remove_tag (tag);
af06d52000-01-12Martin Stjernholm  }
18abb32000-02-15Martin Stjernholm  multiset(Tag) get_runtime_tags()
24f9e82000-08-05Martin Stjernholm  //! Returns all currently active runtime tags.
0ebc9d2000-02-15Martin Stjernholm  {
87fb7e2000-03-25Martin Stjernholm  mapping(string:Tag) tags = runtime_tags;
24f9e82000-08-05Martin Stjernholm  if (new_runtime_tags) tags = new_runtime_tags->filter_tags (tags);
87fb7e2000-03-25Martin Stjernholm  return mkmultiset (values (tags));
0ebc9d2000-02-15Martin Stjernholm  }
bce4d22001-06-19Martin Stjernholm  int incomplete_eval() //! Returns true if the last evaluation isn't complete, i.e. when //! this context is unwound due to use of streaming/nonblocking //! operation. { return unwind_state && unwind_state->reason == "streaming"; }
446bfa2001-06-21Martin Stjernholm  void handle_exception (mixed err, PCode|Parser evaluator, void|int compile_error)
01e43b2000-01-14Martin Stjernholm  //! This function gets any exception that is catched during //! evaluation. evaluator is the object that catched the error.
c757c42000-01-08Martin Stjernholm  {
db04172000-01-14Martin Stjernholm  error_count++;
c065af2001-07-11Martin Stjernholm  if (objectp (err)) { if (err->is_RXML_break_eval) { if (err->action == "continue") return; Context ctx = RXML_CONTEXT; if (ctx->frame) { if (stringp (err->target) ? err->target == ctx->frame->scope_name : err->target == ctx->frame) err->action = "continue";
f5bd9e2001-04-25Martin Nilsson  }
140f362000-10-19Martin Stjernholm  else
c065af2001-07-11Martin Stjernholm  if (err->target) { ctx->frame = err->cur_frame;
6dfc812001-07-20Martin Stjernholm  err = catch (parse_error ("There is no surrounding frame %s.\n",
c065af2001-07-11Martin Stjernholm  stringp (err->target) ? sprintf ("with scope %O", err->target) : sprintf ("%O", err->target))); ctx->frame = 0; handle_exception (err, evaluator, compile_error); } throw (err); } else if (err->is_RXML_Backtrace) { if (evaluator->report_error && evaluator->recover_errors && evaluator->type->free_text) { string msg; if (tag_set && id && id->conf) { msg = err->type == "help" ? err->msg : (err->type == "run" ? ([function(Backtrace,Type:string)] tag_set->handle_run_error) : ([function(Backtrace,Type:string)] tag_set->handle_parse_error) ) ([object(Backtrace)] err, evaluator->type); if(!msg) msg = describe_error(err); } else msg = err->msg; if (evaluator->report_error (msg)) {
6dfc812001-07-20Martin Stjernholm  if (compile_error && evaluator->p_code) { CompiledError comp_err = CompiledError (err); evaluator->p_code->add (RXML_CONTEXT, comp_err, comp_err); }
c065af2001-07-11Martin Stjernholm  return; }
446bfa2001-06-21Martin Stjernholm  }
c065af2001-07-11Martin Stjernholm  throw (err);
01e43b2000-01-14Martin Stjernholm  }
c757c42000-01-08Martin Stjernholm  }
c065af2001-07-11Martin Stjernholm  throw_fatal (err);
c757c42000-01-08Martin Stjernholm  }
6dfc812001-07-20Martin Stjernholm  final array(mixed|PCode) eval_and_compile (Type type, string to_parse, void|int stale_safe)
deecca2001-06-28Martin Stjernholm  //! Parses and evaluates @[to_parse] with @[type] in this context. //! At the same time, p-code is collected to reevaluate it later. An //! array is returned which contains the result in the first element
6dfc812001-07-20Martin Stjernholm  //! and the generated @[RXML.PCode] object in the second. If //! @[stale_safe] is nonzero, the p-code object will be an instance //! of @[RXML.RenewablePCode] instead, which never fails due to //! being stale.
be73272001-06-18Martin Stjernholm  { mixed res; PCode p_code;
9686722001-07-25Martin Stjernholm  int orig_make_p_code = make_p_code, orig_state_updated = state_updated;
6dfc812001-07-20Martin Stjernholm  make_p_code = 1; Parser parser = type->get_parser ( this_object(), tag_set, 0, stale_safe ? RenewablePCode (0) : PCode (0));
02a6562001-07-09Martin Stjernholm  mixed err = catch {
be73272001-06-18Martin Stjernholm  parser->write_end (to_parse); res = parser->eval(); p_code = parser->p_code;
02a6562001-07-09Martin Stjernholm  type->give_back (parser, tag_set);
9686722001-07-25Martin Stjernholm  make_p_code = orig_make_p_code, state_updated = orig_state_updated;
02a6562001-07-09Martin Stjernholm  return ({res, p_code}); }; type->give_back (parser, tag_set);
9686722001-07-25Martin Stjernholm  make_p_code = orig_make_p_code, state_updated = orig_state_updated;
02a6562001-07-09Martin Stjernholm  throw (err);
be73272001-06-18Martin Stjernholm  }
5c7a6b2001-06-25Martin Stjernholm  // Internals:
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  final Parser new_parser (Type top_level_type, void|int _make_p_code)
c6245b1999-12-31Martin Stjernholm  // Returns a new parser object to start parsing with this context. // Normally TagSet.`() should be used instead of this. { #ifdef MODULE_DEBUG
8cb24d2000-02-13Martin Stjernholm  if (in_use || frame) fatal_error ("Context already in use.\n");
c6245b1999-12-31Martin Stjernholm #endif
6dfc812001-07-20Martin Stjernholm  return top_level_type->get_parser (this_object(), tag_set, 0, make_p_code = _make_p_code);
be73272001-06-18Martin Stjernholm  } #ifdef DEBUG private int eval_finished = 0; #endif final void eval_finish() // Called at the end of the evaluation in this context. {
f853b82001-07-19Martin Stjernholm  FRAME_DEPTH_MSG ("%*s%O eval_finish\n", frame_depth, "", this_object()); if (!frame_depth) {
be73272001-06-18Martin Stjernholm #ifdef DEBUG
f853b82001-07-19Martin Stjernholm  if (eval_finished) fatal_error ("Context already finished.\n");
be73272001-06-18Martin Stjernholm  eval_finished = 1; #endif
f853b82001-07-19Martin Stjernholm  if (tag_set) tag_set->call_eval_finish_funs (this_object());
90918b2001-07-11Martin Stjernholm  if (p_code_comp) p_code_comp->compile(); // Fix all delayed resolves.
be73272001-06-18Martin Stjernholm  }
c6245b1999-12-31Martin Stjernholm  }
af06d52000-01-12Martin Stjernholm  mapping(string:SCOPE_TYPE) scopes = ([]);
ed81751999-12-11Martin Stjernholm  // The variable mappings for every currently visible scope. A
3e59342000-01-18Martin Stjernholm  // special entry "_" points to the current local scope.
ed81751999-12-11Martin Stjernholm 
291ade2000-01-25Martin Stjernholm  mapping(Frame:array(SCOPE_TYPE)) hidden = ([]); // The currently hidden scopes. The indices are frame objects which // introduce scopes. The values are tuples of the current scope and // the named scope they hide.
ed81751999-12-11Martin Stjernholm  void enter_scope (Frame frame) { #ifdef DEBUG
d52f8f2001-05-19Martin Stjernholm  if (!frame->vars) fatal_error ("Frame has no variables.\n");
ed81751999-12-11Martin Stjernholm #endif
b2d01b2000-02-15Martin Stjernholm  if (string scope_name = [string] frame->scope_name) { if (!hidden[frame])
291ade2000-01-25Martin Stjernholm  hidden[frame] = ({scopes["_"], scopes[scope_name]});
b2d01b2000-02-15Martin Stjernholm  scopes["_"] = scopes[scope_name] = [SCOPE_TYPE] frame->vars; } else { if (!hidden[frame])
291ade2000-01-25Martin Stjernholm  hidden[frame] = ({scopes["_"], 0});
b2d01b2000-02-15Martin Stjernholm  scopes["_"] = [SCOPE_TYPE] frame->vars; }
ed81751999-12-11Martin Stjernholm  } void leave_scope (Frame frame) {
291ade2000-01-25Martin Stjernholm  if (array(SCOPE_TYPE) back = hidden[frame]) { if (SCOPE_TYPE cur = back[0]) scopes["_"] = cur; else m_delete (scopes, "_"); if (SCOPE_TYPE named = back[1]) { #ifdef MODULE_DEBUG if (!stringp (frame->scope_name))
8cb24d2000-02-13Martin Stjernholm  fatal_error ("Scope named changed to %O during parsing.\n", frame->scope_name);
291ade2000-01-25Martin Stjernholm #endif scopes[[string] frame->scope_name] = named;
ed81751999-12-11Martin Stjernholm  }
291ade2000-01-25Martin Stjernholm  else m_delete (scopes, [string] frame->scope_name); m_delete (hidden, frame);
ed81751999-12-11Martin Stjernholm  } }
d52f8f2001-05-19Martin Stjernholm #define ENTER_SCOPE(ctx, frame) \ (frame->vars && frame->vars != ctx->scopes["_"] && ctx->enter_scope (frame)) #define LEAVE_SCOPE(ctx, frame) \ (frame->vars && ctx->leave_scope (frame))
ed81751999-12-11Martin Stjernholm 
87fb7e2000-03-25Martin Stjernholm  mapping(string:Tag) runtime_tags = ([]);
24f9e82000-08-05Martin Stjernholm  // The active runtime tags. PI tags are stored in the same mapping // with their names prefixed by '?'.
0ebc9d2000-02-15Martin Stjernholm  NewRuntimeTags new_runtime_tags;
c6245b1999-12-31Martin Stjernholm  // Used to record the result of any add_runtime_tag() and // remove_runtime_tag() calls since the last time the parsers ran.
ed81751999-12-11Martin Stjernholm 
fb605b2001-07-11Martin Stjernholm  int make_p_code; // Nonzero if the parsers should compile along with the evaluation.
9686722001-07-25Martin Stjernholm  int state_updated; // Nonzero if the persistent state of the evaluated rxml has // changed.
fb605b2001-07-11Martin Stjernholm  PikeCompile p_code_comp; // The PikeCompile object for collecting any produce Pike byte code // during p-code compilation.
6570bf2001-06-29Martin Stjernholm  static void create (void|TagSet _tag_set, void|RequestID _id)
ed81751999-12-11Martin Stjernholm  // Normally TagSet.`() should be used instead of this. {
02a6562001-07-09Martin Stjernholm  tag_set = _tag_set || empty_tag_set;
ed81751999-12-11Martin Stjernholm  id = _id;
f02d002000-03-18Martin Stjernholm #ifdef RXML_OBJ_DEBUG __object_marker->create (this_object()); #endif
ed81751999-12-11Martin Stjernholm  }
7dd3f82001-04-18Martin Stjernholm  UNWIND_STATE unwind_state;
ed81751999-12-11Martin Stjernholm  // If this is a mapping, we have an unwound stack state. It contains
56532d1999-12-19Martin Stjernholm  // strings with arbitrary exception info, and the objects being // unwound with arrays containing the extra state info they need. // The first entry in these arrays are always the subobject. The // special entries are:
ed81751999-12-11Martin Stjernholm  //
56532d1999-12-19Martin Stjernholm  // "top": ({Frame|Parser|PCode (top object)})
ed81751999-12-11Martin Stjernholm  // "stream_piece": mixed (When continuing, do a streaming
91c8982000-03-11Martin Stjernholm  // do_process() with this stream piece.)
ed81751999-12-11Martin Stjernholm  // "exec_left": array (Exec array left to evaluate. Only used // between Frame._exec_array() and Frame._eval().)
bce4d22001-06-19Martin Stjernholm  // "reason": string (The reason why the state is unwound. Can // currently be "streaming".)
ed81751999-12-11Martin Stjernholm 
112b012001-08-10Martin Nilsson  //! @ignore
f02d002000-03-18Martin Stjernholm  MARK_OBJECT_ONLY;
112b012001-08-10Martin Nilsson  //! @endignore
2bd21a2000-01-05Martin Stjernholm 
f02d002000-03-18Martin Stjernholm  string _sprintf() {return "RXML.Context" + OBJ_COUNT;}
ec027c2000-03-16Martin Stjernholm 
ed81751999-12-11Martin Stjernholm #ifdef MODULE_DEBUG
2d20342000-02-12Martin Stjernholm #if constant (thread_create) Thread.Thread in_use; #else
ed81751999-12-11Martin Stjernholm  int in_use; #endif
2d20342000-02-12Martin Stjernholm #endif
ed81751999-12-11Martin Stjernholm }
ec027c2000-03-16Martin Stjernholm static class NewRuntimeTags
24f9e82000-08-05Martin Stjernholm // Tool class used to track runtime tags in Context.
ec027c2000-03-16Martin Stjernholm {
24f9e82000-08-05Martin Stjernholm  static mapping(string:Tag) add_tags; static mapping(string:int|string) remove_tags; void add_tag (Tag tag) { if (!add_tags) add_tags = ([]); if (tag->flags & FLAG_PROC_INSTR) { add_tags["?" + tag->name] = tag; // By doing the following, we can let remove_proc_instrs take precedence. if (remove_tags) m_delete (remove_tags, "?" + tag->name); } else { add_tags[tag->name] = tag; if (remove_tags) m_delete (remove_tags, tag->name); } } void remove_tag (string name, int proc_instr) { if (!remove_tags) remove_tags = ([]); if (proc_instr) remove_tags["?" + name] = name; else remove_tags[name] = 1; } array(Tag) added_tags() { if (!add_tags) return ({}); if (remove_tags) return values (add_tags - remove_tags); return values (add_tags); } array(string) removed_tags() { return remove_tags ? indices (filter (remove_tags, intp)) : ({}); } array(string) removed_pi_tags() { return remove_tags ? values (remove_tags) - ({1}) : ({}); } mapping(string:Tag) filter_tags (mapping(string:Tag) tags) { if (add_tags) tags |= add_tags; if (remove_tags) tags -= remove_tags; return tags; }
ec027c2000-03-16Martin Stjernholm }
c065af2001-07-11Martin Stjernholm static class BreakEval (Frame|string target) // Used in frame break exceptions. { constant is_RXML_BreakEval = 1; string action = "break"; Frame cur_frame = RXML_CONTEXT->frame; }
8cb24d2000-02-13Martin Stjernholm class Backtrace //! The object used to throw RXML errors. { constant is_generic_error = 1; constant is_RXML_Backtrace = 1;
0ebc9d2000-02-15Martin Stjernholm  string type; // Currently "run" or "parse".
8cb24d2000-02-13Martin Stjernholm  string msg; Context context;
80e42c2001-06-26Martin Stjernholm  array(Frame) frames;
02a6562001-07-09Martin Stjernholm  array(mapping(string:mixed)) args;
8cb24d2000-02-13Martin Stjernholm  string current_var; array backtrace;
6570bf2001-06-29Martin Stjernholm  static void create (void|string _type, void|string _msg, void|Context _context, void|array _backtrace)
8cb24d2000-02-13Martin Stjernholm  { type = _type; msg = _msg;
80e42c2001-06-26Martin Stjernholm  if (context = _context || RXML_CONTEXT) { frames = allocate (context->frame_depth);
02a6562001-07-09Martin Stjernholm  args = allocate (context->frame_depth);
80e42c2001-06-26Martin Stjernholm  Frame frame = context->frame; int i = 0;
02a6562001-07-09Martin Stjernholm  for (; frame; i++, frame = frame->up) {
f9e8b12001-08-09Martin Stjernholm  if (i >= sizeof (frames)) { frames += allocate (sizeof (frames) + 1); args += allocate (sizeof (args) + 1); }
02a6562001-07-09Martin Stjernholm  frames[i] = frame; args[i] = frame->args; }
80e42c2001-06-26Martin Stjernholm  frames = frames[..i - 1];
02a6562001-07-09Martin Stjernholm  args = args[..i - 1];
80e42c2001-06-26Martin Stjernholm  }
51f88b2000-06-23Martin Stjernholm  if (_backtrace) backtrace = _backtrace; else { backtrace = predef::backtrace(); backtrace = backtrace[..sizeof (backtrace) - 2]; }
8cb24d2000-02-13Martin Stjernholm  } string describe_rxml_backtrace (void|int no_msg) //! Returns a formatted RXML frame backtrace. {
a08cd72001-06-09Martin Stjernholm  String.Buffer txt = String.Buffer();
893ac12001-06-21Martin Stjernholm  function(string...:void) add = txt->add;
09a16e2001-07-10Martin Stjernholm  if (!no_msg) add ("RXML", type ? " " + type : "", " error");
8cb24d2000-02-13Martin Stjernholm  if (context) {
893ac12001-06-21Martin Stjernholm  if (!no_msg) add (": ", msg || "(no error message)\n"); if (current_var) add (" | &", current_var, ";\n");
02a6562001-07-09Martin Stjernholm  for (int i = 0; i < sizeof (frames); i++) { Frame f = frames[i];
24f9e82000-08-05Martin Stjernholm  string name; if (f->tag) name = f->tag->name;
80e42c2001-06-26Martin Stjernholm  //else if (!f->up) break;
24f9e82000-08-05Martin Stjernholm  else name = "(unknown)"; if (f->flags & FLAG_PROC_INSTR)
893ac12001-06-21Martin Stjernholm  add (" | <?", name, "?>\n");
24f9e82000-08-05Martin Stjernholm  else {
893ac12001-06-21Martin Stjernholm  add (" | <", name);
02a6562001-07-09Martin Stjernholm  mapping(string:mixed) argmap = args[i]; if (mappingp (argmap)) foreach (sort (indices (argmap)), string arg) { mixed val = argmap[arg];
893ac12001-06-21Martin Stjernholm  add (" ", arg, "=");
446bfa2001-06-21Martin Stjernholm  if (arrayp (val)) add (map (val, error_print_val) * ","); else add (error_print_val (val));
24f9e82000-08-05Martin Stjernholm  }
446bfa2001-06-21Martin Stjernholm  else add (" (no argmap)"); add (">\n");
24f9e82000-08-05Martin Stjernholm  }
8cb24d2000-02-13Martin Stjernholm  } } else
893ac12001-06-21Martin Stjernholm  if (!no_msg) add (" (no context): ", msg || "(no error message)\n");
a08cd72001-06-09Martin Stjernholm  return txt->get();
8cb24d2000-02-13Martin Stjernholm  } private string error_print_val (mixed val) { if (arrayp (val)) return "array"; else if (mappingp (val)) return "mapping"; else if (multisetp (val)) return "multiset"; else return sprintf ("%O", val); } string|array `[] (int i) { switch (i) { case 0: return describe_rxml_backtrace(); case 1: return backtrace; } }
6dfc812001-07-20Martin Stjernholm  mixed `[]= (int i, mixed val) { if (i == 0 && stringp (val)) { // Try to handle additional info being set in the error message. // This is very icky. The exception interface could be better.. :P string oldmsg = describe_rxml_backtrace(); if (has_prefix (val, oldmsg)) msg += val[sizeof (oldmsg)..]; else if (has_suffix (val, oldmsg)) msg = val[..sizeof (val) - sizeof (oldmsg) - 1] + msg; else msg = val; return val; } error ("Cannot set index %O to %O.\n", i, val); }
1c73f62000-03-04Martin Stjernholm  string _sprintf() {return "RXML.Backtrace(" + (type || "") + ")";}
8cb24d2000-02-13Martin Stjernholm }
ed81751999-12-11Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm // Current context:
ed81751999-12-11Martin Stjernholm 
be73272001-06-18Martin Stjernholm final void set_context (Context ctx) {SET_RXML_CONTEXT (ctx);} final Context get_context() {return [object(Context)] RXML_CONTEXT;} //! Returns the current @[RXML.Context] object, which contains all the //! evaluation context info. It's updated before any function in //! @[RXML.Tag] or @[RXML.Frame] is called. //! //! @note //! A slightly faster way to access it is through the @[RXML_CONTEXT] //! macro in @tt{module.h@}.
2d20342000-02-12Martin Stjernholm #if defined (MODULE_DEBUG) && constant (thread_create)
ed81751999-12-11Martin Stjernholm  // Got races in this debug check, but looks like we have to live with that. :\ #define ENTER_CONTEXT(ctx) \
be73272001-06-18Martin Stjernholm  Context __old_ctx = RXML_CONTEXT; \ SET_RXML_CONTEXT (ctx); \
ed81751999-12-11Martin Stjernholm  if (ctx) { \
2d20342000-02-12Martin Stjernholm  if (ctx->in_use && ctx->in_use != this_thread()) \
8cb24d2000-02-13Martin Stjernholm  fatal_error ("Attempt to use context asynchronously.\n"); \
2d20342000-02-12Martin Stjernholm  ctx->in_use = this_thread(); \
446bfa2001-06-21Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm  #define LEAVE_CONTEXT() \
be73272001-06-18Martin Stjernholm  if (Context ctx = RXML_CONTEXT) \
ed81751999-12-11Martin Stjernholm  if (__old_ctx != ctx) ctx->in_use = 0; \
be73272001-06-18Martin Stjernholm  SET_RXML_CONTEXT (__old_ctx);
ed81751999-12-11Martin Stjernholm  #else #define ENTER_CONTEXT(ctx) \
be73272001-06-18Martin Stjernholm  Context __old_ctx = RXML_CONTEXT; \
446bfa2001-06-21Martin Stjernholm  SET_RXML_CONTEXT (ctx);
ed81751999-12-11Martin Stjernholm  #define LEAVE_CONTEXT() \
be73272001-06-18Martin Stjernholm  SET_RXML_CONTEXT (__old_ctx);
ed81751999-12-11Martin Stjernholm  #endif
76cbfb2000-02-04Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm // Constants for the bit field RXML.Frame.flags.
ed81751999-12-11Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_NONE = 0x00000000;
c159ec2000-02-04Martin Stjernholm //! The no-flags flag. In case you think 0 is too ugly. ;)
ac69772000-09-08Martin Stjernholm 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.
4293072001-05-08Martin Stjernholm //! Note that @tt{DEBUG@} must be defined for the debug printouts to //! be compiled in (normally enabled with the @tt{--debug@} flag to //! Roxen).
ac69772000-09-08Martin Stjernholm 
deb79a2001-06-26Martin Stjernholm // Flags tested in the Tag object:
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm 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. //! //! This flag may be changed in @[do_enter] to turn enable the error //! check if the tag contains content.
24f9e82000-08-05Martin Stjernholm constant FLAG_PROC_INSTR = 0x00000010; //! Flags this as a processing instruction tag (i.e. one parsed with
4293072001-05-08Martin Stjernholm //! 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.
ed81751999-12-11Martin Stjernholm 
51f88b2000-06-23Martin Stjernholm constant FLAG_COMPAT_PARSE = 0x00000002;
4293072001-05-08Martin Stjernholm //! 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.
51f88b2000-06-23Martin Stjernholm  constant FLAG_NO_PREFIX = 0x00000004;
cece9e2000-01-11Martin Stjernholm //! Never apply any prefix to this tag.
51f88b2000-06-23Martin Stjernholm constant FLAG_SOCKET_TAG = 0x00000008;
c159ec2000-02-04Martin Stjernholm //! Declare the tag to be a socket tag, which accepts plugin tags (see
4293072001-05-08Martin Stjernholm //! @[RXML.Tag.plugin_name] for details).
c159ec2000-02-04Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_DONT_PREPARSE = 0x00000040;
4293072001-05-08Martin Stjernholm //! 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.
c159ec2000-02-04Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_POSTPARSE = 0x00000080;
4293072001-05-08Martin Stjernholm //! Postparse the result with the @[RXML.PXml] parser. This is only //! used in the simple tag wrapper. Defined here as placeholder.
c159ec2000-02-04Martin Stjernholm 
deb79a2001-06-26Martin Stjernholm // Flags tested in the Frame object:
ed81751999-12-11Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_STREAM_RESULT = 0x00000400;
4293072001-05-08Martin Stjernholm //! If set, the @[do_process] function will be called repeatedly until
ed81751999-12-11Martin Stjernholm //! it returns 0 or no more content is wanted.
8cb24d2000-02-13Martin Stjernholm constant FLAG_STREAM_CONTENT = 0x00000800;
ed81751999-12-11Martin Stjernholm //! If set, the tag supports getting its content in streaming mode:
4293072001-05-08Martin Stjernholm //! @[do_process] will be called repeatedly with successive parts of //! the content then. Can't be changed from @[do_process]. //!
151fa92001-04-19Johan Sundström //! @note //! It might be obvious, but using streaming is significantly less //! effective than nonstreaming, so it should only be done when big //! delays are expected.
ed81751999-12-11Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_STREAM = FLAG_STREAM_RESULT | FLAG_STREAM_CONTENT;
ed81751999-12-11Martin Stjernholm 
8cb24d2000-02-13Martin Stjernholm constant FLAG_UNPARSED = 0x00001000;
4293072001-05-08Martin Stjernholm //! 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]).
49897a2000-02-13Martin Stjernholm 
140f362000-10-19Martin Stjernholm constant FLAG_DONT_RECOVER = 0x00002000; //! If set, RXML errors are never recovered when parsing the content //! in the tag. If any occurs, it will instead abort the execution of //! this tag too to propagate the error to the parent tag.
4d62ba2000-08-15Martin Stjernholm //!
140f362000-10-19Martin Stjernholm //! When an error occurs, the parser aborts tags upward in the frame
4d62ba2000-08-15Martin Stjernholm //! stack until it comes to one which looks like it can accept an
140f362000-10-19Martin Stjernholm //! error report in its content. The parser then reports the error //! there and continues.
4d62ba2000-08-15Martin Stjernholm //!
140f362000-10-19Martin Stjernholm //! The criteria for the frame which will handle the error recovery is
4293072001-05-08Martin Stjernholm //! 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.
140f362000-10-19Martin Stjernholm  constant FLAG_DONT_REPORT_ERRORS = FLAG_DONT_RECOVER; // For compatibility.
4d62ba2000-08-15Martin Stjernholm  constant FLAG_RAW_ARGS = 0x00004000;
0c9e5b2001-03-13Martin Stjernholm //! Special flag to @[RXML.t_xml.format_tag]; only defined here as a //! placeholder. When this is given to @[RXML.t_xml.format_tag], it //! 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
4293072001-05-08Martin Stjernholm //! been parsed by @[Parser.HTML] (or @[parse_html]) but hasn't been
9a38062000-08-04Martin Stjernholm //! processed further.
02a6562001-07-09Martin Stjernholm constant FLAG_COMPILE_INPUT = 0x00008000; //! The arguments and the content of the frame is always compiled to //! p-code if this is set. Otherwise it's only done if the surrounding //! content is, or if the frame iterates more than once.
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm constant FLAG_COMPILE_RESULT = 0x00010000; //! Any evaluation done in the exec arrays that the frame callbacks //! returns is also compiled to p-code, and the exec array is //! destructively changed to contain the p-code. This affects strings
6923692001-07-16Martin Stjernholm //! if the result type has a parser.
ed81751999-12-11Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm constant FLAG_GET_RAW_CONTENT = 0x00020000; //! Puts the unparsed content of the tag into the //! @[RXML.Frame.content] variable when @[RXML.Frame.do_enter] is //! called. It's only available when the tag is actually evaluated //! from source, however (as opposed to @[RXML.Frame.raw_tag_text]); a //! cached frame won't receive it, but it will otoh contain the state //! from an earlier evaluation from source (see @[RXML.Frame.save] and //! @[RXML.Frame.restore]). constant FLAG_GET_EVALED_CONTENT = 0x00040000; //! When the content is evaluated, the frame will receive the result //! of the evaluation as p-code in @[RXML.Frame.evaled_content], with //! the exception of any nested tags which got //! @[FLAG_DONT_CACHE_RESULT] set. constant FLAG_DONT_CACHE_RESULT = 0x00080000; //! Keep this frame unevaluated in the p-code produced for a //! surrounding frame with @[FLAG_GET_EVALED_CONTENT]. That implies //! that all other surrounding frames also remain unevaluated, and //! this flag is therefore automatically propagated by the parser into //! surrounding frames. The flag is tested after the first evaluation //! of the frame has finished. // 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. // // This feature proved unnecessary and no longer exists. // constant FLAG_NO_IMPLICIT_ARGS = 0x00000200; // // If set, the parser won't apply any implicit arguments. // // Not implemented since there has been no need for it. The only // implicit argument is "help" (see also MAGIC_HELP_ARG), and there // probably won't be any more.
ed81751999-12-11Martin Stjernholm class Frame
46c68f2000-08-12Martin Stjernholm //! A tag instance. A new frame is normally created for every parsed //! tag in the source document. It might be reused both when the //! document is requested again and when the tag is reevaluated in a
69e4a42001-08-14Martin Stjernholm //! loop, but it's not certain in either case (see also @[save] and //! @[restore]). Therefore, be careful about using variable //! initializers.
ed81751999-12-11Martin Stjernholm { constant is_RXML_Frame = 1;
c1ee002001-06-29Martin Stjernholm  constant is_RXML_encodable = 1;
02a6562001-07-09Martin Stjernholm  constant is_RXML_p_code_frame = 1;
cf042f2001-07-21Martin Stjernholm  constant is_RXML_p_code_entry = 1;
56532d1999-12-19Martin Stjernholm  constant thrown_at_unwind = 1;
ed81751999-12-11Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm  // Interface:
ed81751999-12-11Martin Stjernholm  Frame up; //! The parent frame. This frame is either created from the content
91c8982000-03-11Martin Stjernholm  //! inside the up frame, or it's in an exec array produced by the up //! frame.
ed81751999-12-11Martin Stjernholm  Tag tag;
d4768a2001-05-18Martin Stjernholm  //! The @[RXML.Tag] object this frame was created from.
ed81751999-12-11Martin Stjernholm  int flags;
d4768a2001-05-18Martin Stjernholm  //! Various bit flags that affect parsing. See the @tt{FLAG_*@} //! constants. It's copied from @[Tag.flag] when the frame is //! created.
deb79a2001-06-26Martin Stjernholm  //! //! @note //! This variable may be set in the @tt{do_*@} callbacks, but it's //! assumed to be static, i.e. its value should not depend on any //! information that's known only at runtime. Practically that means //! that the value is assumed to never change if the frame is reused //! by p-code.
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  mapping(string:mixed)|EVAL_ARGS_FUNC args;
6dfc812001-07-20Martin Stjernholm  //! A mapping with the (parsed and evaluated) arguments passed to //! the tag. Set every time the frame is executed, before any frame //! callbacks are called (unless the frame was made with the //! finished args mapping directly, e.g. by @[RXML.make_tag]). Not //! set for processing instruction (@[FLAG_PROC_INSTR]) tags.
ed81751999-12-11Martin Stjernholm  Type content_type;
6dfc812001-07-20Martin Stjernholm  //! The type of the content. It may be changed in @[do_enter] to //! affect how the content will be parsed. //! //! @note //! This variable is assumed to be static in the sense that its //! value does not depend on any information that's known only at //! runtime. Practically that means that the value is assumed to //! never change if the frame is compiled into p-code. //! //! Note that the assumption can't always be guaranteed. E.g. if the //! content type can be set by an @[RXML.Type] argument to the tag, //! it's always possible that it's set through a splice argument //! that might change at run time. Since that seems only like a //! theoretical situation, and since solving it would incur a //! runtime cost, it's been left as a "known issue" for the time //! being.
ed81751999-12-11Martin Stjernholm 
deb79a2001-06-26Martin Stjernholm  mixed content;
d4768a2001-05-18Martin Stjernholm  //! The content, if any. Set before @[do_process] and @[do_return] //! are called. Initialized to @[RXML.nil] every time the frame
02a6562001-07-09Martin Stjernholm  //! executed (unless the frame was made with the finished content //! directly, e.g. by @[RXML.make_tag]).
ed81751999-12-11Martin Stjernholm  Type result_type;
46c68f2000-08-12Martin Stjernholm  //! The required result type. If it has a parser, it will affect how //! execution arrays are handled; see the return value for
d4768a2001-05-18Martin Stjernholm  //! @[do_return] for details.
46c68f2000-08-12Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! This is set by the type inference from @[Tag.result_types] before
46c68f2000-08-12Martin Stjernholm  //! any frame callbacks are called. The frame may change this type, //! but it must produce a result value which matches it. The value //! is converted before being inserted into the parent content if //! necessary. An exception (which this frame can't catch) is thrown //! if conversion is impossible.
6dfc812001-07-20Martin Stjernholm  //! //! @note //! This variable is assumed to be static; see the note for //! @[content_type] for further details.
46c68f2000-08-12Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  mixed result;
d4768a2001-05-18Martin Stjernholm  //! The result, which is assumed to be either @[RXML.nil] or a valid
46c68f2000-08-12Martin Stjernholm  //! value according to result_type. The exec arrays returned by e.g.
d4768a2001-05-18Martin Stjernholm  //! @[do_return] changes this. It may also be set directly. //! Initialized to @[RXML.nil] every time the frame executed.
46c68f2000-08-12Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! If @[result_type] has a parser set, it will be used by //! @[do_return] etc before assigning to this variable. Thus it //! contains the value after any parsing and will not be parsed //! again.
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional mapping(string:mixed) vars;
d4768a2001-05-18Martin Stjernholm  //!
ed81751999-12-11Martin Stjernholm  //! Set this to introduce a new variable scope that will be active
f7469d2001-07-10Martin Stjernholm  //! during parsing of the content and return values.
6dfc812001-07-20Martin Stjernholm  //! //! @note //! A frame may destructively change or replace its own @[vars] //! mapping to make changes in its scope. Changes from any other //! place should go through @[RXML.Context.set_var] (or its //! alternatives @[RXML.Context.user_set_var], @[RXML.set_var], //! @[set_var] etc) so that the change is recorded properly in //! caches etc.
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional string scope_name;
d4768a2001-05-18Martin Stjernholm  //!
291ade2000-01-25Martin Stjernholm  //! The scope name for the variables. Must be set before the scope //! is used for the first time, and can't be changed after that.
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional TagSet additional_tags;
d4768a2001-05-18Martin Stjernholm  //!
ed81751999-12-11Martin Stjernholm  //! If set, the tags in this tag set will be used in addition to the //! tags inherited from the surrounding parser. The additional tags //! will in turn be inherited by subparsers.
deb79a2001-06-26Martin Stjernholm  //! //! @note //! This variable may be set in the @[do_enter] callback, but it's //! assumed to be static, i.e. its value should not depend on any //! information that's known only at runtime. Practically that means //! that the value is assumed to never change if the frame is reused //! by p-code.
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional TagSet local_tags;
d4768a2001-05-18Martin Stjernholm  //!
ed81751999-12-11Martin Stjernholm  //! If set, the tags in this tag set will be used in the parser for //! the content, instead of the one inherited from the surrounding //! parser. The tags are not inherited by subparsers.
deb79a2001-06-26Martin Stjernholm  //! //! @note
6dfc812001-07-20Martin Stjernholm  //! This variable is assumed to be static; see the note for //! @[additional_tags] for further details.
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional Frame parent_frame;
d4768a2001-05-18Martin Stjernholm  //!
689dc62000-08-31Martin Stjernholm  //! If this variable exists, it gets set to the frame object of the //! closest surrounding tag that defined this tag in its
d4768a2001-05-18Martin Stjernholm  //! @[additional_tags] or @[local_tags]. Useful to access the //! "mother tag" from the subtags it defines.
689dc62000-08-31Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional string raw_tag_text;
d4768a2001-05-18Martin Stjernholm  //!
51f88b2000-06-23Martin Stjernholm  //! If this variable exists, it gets the raw text representation of //! the tag, if there is any. Note that it's after parsing of any //! splice argument.
deb79a2001-06-26Martin Stjernholm  //! //! @note //! This variable is assumed to be static, i.e. its value doesn't //! depend on any information that's known only at runtime.
51f88b2000-06-23Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional array do_enter (RequestID id); //! @decl optional array do_process (RequestID id, void|mixed piece); //! @decl optional array do_return (RequestID id);
291ade2000-01-25Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! @[do_enter] is called first thing when processing the tag. //! @[do_process] is called after (some of) the content has been //! processed. @[do_return] is called lastly before leaving the tag. //! //! For tags that loops more than one time (see @[do_iterate]): //! @[do_enter] is only called initially before the first call to //! @[do_iterate]. @[do_process] is called after each iteration. //! @[do_return] is called after the last call to @[do_process].
ed81751999-12-11Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! The @[result_type] variable is set to the type of result the //! parser wants. The tag may change it; the value will then be //! converted to the type that the parser wants. If the result type //! is sequential, it's added to the surrounding content, otherwise //! it is used as value of the content, and there's an error of the //! content has a value already. If the result is @[RXML.nil], it //! does not affect the surrounding content at all.
ed81751999-12-11Martin Stjernholm  //! //! Return values:
893ac12001-06-21Martin Stjernholm  //! @dl
151fa92001-04-19Johan Sundström  //! @item array
d4768a2001-05-18Martin Stjernholm  //! A so-called exec array to be handled by the parser. The
151fa92001-04-19Johan Sundström  //! elements are processed in order, and have the following usage:
893ac12001-06-21Martin Stjernholm  //! @dl
151fa92001-04-19Johan Sundström  //! @item string //! Added or put into the result. If the result type has a //! parser, the string will be parsed with it before it's //! assigned to the result variable and passed on.
f7469d2001-07-10Martin Stjernholm  //! @item @[RXML.Frame]
151fa92001-04-19Johan Sundström  //! Already initialized frame to process. Neither arguments nor //! content will be parsed. It's result is added or put into the
893ac12001-06-21Martin Stjernholm  //! result of this tag. The functions @[RXML.make_tag],
02a6562001-07-09Martin Stjernholm  //! @[RXML.make_unparsed_tag] are useful to create frames.
f7469d2001-07-10Martin Stjernholm  //! @item @[RXML.PCode]
02a6562001-07-09Martin Stjernholm  //! A p-code object to evaluate. It's not necessary that the //! type it evaluates to is the same as @[result_type]; it will //! be converted if it isn't.
f7469d2001-07-10Martin Stjernholm  //! @item function(RequestID:mixed) //! Run the function and add its return value to the result. //! It's assumed to be a valid value of @[result_type].
151fa92001-04-19Johan Sundström  //! @item object //! Treated as a file object to read in blocking or nonblocking //! mode. FIXME: Not yet implemented, details not decided. //! @item multiset(mixed) //! Should only contain one element that'll be added or put into //! the result. Normally not necessary; assign it directly to //! the result variable instead. //! @item propagate_tag() //! Use a call to this function to propagate the tag to be //! handled by an overridden tag definition, if any exists. If //! this is used, it's probably necessary to define the
d4768a2001-05-18Martin Stjernholm  //! @[raw_tag_text] variable. For further details see the doc //! for @[propagate_tag] in this class.
f7469d2001-07-10Martin Stjernholm  //! @item @[RXML.nil] //! Ignored.
893ac12001-06-21Martin Stjernholm  //! @enddl
151fa92001-04-19Johan Sundström  //! @item 0 //! Do nothing special. Exits the tag when used from
d4768a2001-05-18Martin Stjernholm  //! @[do_process] and @[FLAG_STREAM_RESULT] is set.
893ac12001-06-21Martin Stjernholm  //! @enddl
291ade2000-01-25Martin Stjernholm  //! //! Note that the intended use is not to postparse by setting a //! parser on the result type, but instead to return an array with
d4768a2001-05-18Martin Stjernholm  //! literal strings and @[RXML.Frame] objects where parsing (or, //! more accurately, evaluation) needs to be done.
291ade2000-01-25Martin Stjernholm  //! //! If an array instead of a function is given, the array is handled
d4768a2001-05-18Martin Stjernholm  //! as above. If the result variable is @[RXML.nil] (which it //! defaults to), @[content] is used as @[result] if it's of a //! compatible type.
291ade2000-01-25Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! If there is no @[do_return] and the result from parsing the //! content is not @[RXML.nil], it's assigned to or added to the //! @[result] variable. Assignment is used if the content type is //! nonsequential, addition otherwise.
ed81751999-12-11Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! Regarding @[do_process] only:
9477392000-03-20Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! Normally the @[content] variable is set to the parsed content of //! the tag before @[do_process] is called. This may be @[RXML.nil] //! if the content parsing didn't produce any result.
9477392000-03-20Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! @[piece] is used when the tag is operating in streaming mode //! (i.e. @[FLAG_STREAM_CONTENT] is set). It's then set to each //! successive part of the content in the stream, and the @[content] //! variable is never touched. @[do_process] is also called //! "normally" with no @[piece] argument afterwards. Note that tags //! that support streaming mode might still be used nonstreaming (it //! might also vary between iterations).
ed81751999-12-11Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! As long as @[FLAG_STREAM_RESULT] is set, @[do_process] will be
91c8982000-03-11Martin Stjernholm  //! called repeatedly until it returns 0. It's only the result piece //! from the execution array that is propagated after each turn; the
ed81751999-12-11Martin Stjernholm  //! result variable only accumulates all these pieces.
291ade2000-01-25Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  //! @decl optional int do_iterate (RequestID id);
d4768a2001-05-18Martin Stjernholm  //!
291ade2000-01-25Martin Stjernholm  //! Controls the number of passes in the tag done by the parser. In //! every pass, the content of the tag (if any) is processed, then
d4768a2001-05-18Martin Stjernholm  //! @[do_process] is called.
291ade2000-01-25Martin Stjernholm  //!
d4768a2001-05-18Martin Stjernholm  //! Before doing any pass, @[do_iterate] is called. If the return //! value is nonzero, that many passes is done, then @[do_iterate] //! is called again and the process repeats. If the return value is //! zero, the tag exits and the value in @[result] is used in the
291ade2000-01-25Martin Stjernholm  //! surrounding content as described above.
ed81751999-12-11Martin Stjernholm  //!
291ade2000-01-25Martin Stjernholm  //! The most common way to iterate is to do the setup before every //! pass (e.g. setup the variable scope) and return 1 to do one pass //! through the content. This will repeat until 0 is returned. //!
d4768a2001-05-18Martin Stjernholm  //! If @[do_iterate] is a positive integer, that many passes is done //! and then the tag exits. If @[do_iterate] is zero or missing, one //! pass is done. If @[do_iterate] is negative, no pass is done.
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  optional mixed save(); //! If defined, this will be called after the frame has been
9686722001-07-25Martin Stjernholm  //! evaluated (i.e. after @[do_return]) for the first time, if the //! frame state is to be preserved in p-code. The returned value, //! aka the persistent state of the frame, will be passed to
02a6562001-07-09Martin Stjernholm  //! @[restore] when the frame is reinstantiated from the p-code.
d4768a2001-05-18Martin Stjernholm  //!
02a6562001-07-09Martin Stjernholm  //! The function usually saves the frame specific data that should //! be cached. It need not save the values of the standard variables //! @[flags], @[args], @[content], @[content_type], @[result_type] //! and @[raw_tag_text] (if present). Note that when this function //! is called, @[args] and @[content] are set to the function and //! p-code, respectively, used for reevaluation of those values.
deb79a2001-06-26Martin Stjernholm  //!
9686722001-07-25Martin Stjernholm  //! If the persistent state changes in a later reevaluation of the //! frame, it should call @[RXML.Context.state_update] to trig //! another save of the frame state. //!
02a6562001-07-09Martin Stjernholm  //! If this returns zero then @[restore] won't be called. optional void restore (mixed saved); //! Should be defined when @[save] is. It takes the value produced //! by @[save] and restores that frame state. The values of the //! standard variables are already restored to the same values they //! had at the call to @[save].
deb79a2001-06-26Martin Stjernholm  //!
02a6562001-07-09Martin Stjernholm  //! @note
6dfc812001-07-20Martin Stjernholm  //! A frame might be reevaluated without a prior call to this //! function, if it's the same frame object since the call to //! @[save].
02a6562001-07-09Martin Stjernholm  //! //! @note //! This function is used to decode dumped p-code that's read from //! disk. @[saved] might therefore be of an incompatible format //! produced by an earlier version of @[save]. It's not necessary //! that @[restore] handles this, but if it doesn't it must produce //! some kind of exception so that the decoding of the p-code fails. //! It must never restore an invalid state which might cause errors //! or invalid results in later calls to the @tt{do_*@} functions.
ed81751999-12-11Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  //! @decl PCode evaled_content;
fb605b2001-07-11Martin Stjernholm  //!
6dfc812001-07-20Martin Stjernholm  //! Must exist if @[FLAG_GET_EVALED_CONTENT] is set. It will be set //! to a p-code object containing the (mostly) evaluated content (in //! each iteration). This variable is not automatically saved and //! restored (see @[save] and @[restore]).
fb605b2001-07-11Martin Stjernholm 
5c7a6b2001-06-25Martin Stjernholm  // Services:
ed81751999-12-11Martin Stjernholm 
74fa382001-07-25Martin Stjernholm  final mixed get_var (string|array(string|int) var, void|string scope_name, void|Type want_type)
d4768a2001-05-18Martin Stjernholm  //! A wrapper for easy access to @[RXML.Context.get_var].
ffa6932000-02-20Martin Stjernholm  {
be73272001-06-18Martin Stjernholm  return RXML_CONTEXT->get_var (var, scope_name, want_type);
ffa6932000-02-20Martin Stjernholm  }
74fa382001-07-25Martin Stjernholm  final mixed set_var (string|array(string|int) var, mixed val, void|string scope_name)
d4768a2001-05-18Martin Stjernholm  //! A wrapper for easy access to @[RXML.Context.set_var].
ffa6932000-02-20Martin Stjernholm  {
be73272001-06-18Martin Stjernholm  return RXML_CONTEXT->set_var (var, val, scope_name);
ffa6932000-02-20Martin Stjernholm  }
74fa382001-07-25Martin Stjernholm  final void delete_var (string|array(string|int) var, void|string scope_name)
d4768a2001-05-18Martin Stjernholm  //! A wrapper for easy access to @[RXML.Context.delete_var].
ffa6932000-02-20Martin Stjernholm  {
be73272001-06-18Martin Stjernholm  RXML_CONTEXT->delete_var (var, scope_name);
ffa6932000-02-20Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  void run_error (string msg, mixed... args)
d4768a2001-05-18Martin Stjernholm  //! A wrapper for easy access to @[RXML.run_error].
ed81751999-12-11Martin Stjernholm  {
5ed0132000-02-13Martin Stjernholm  _run_error (msg, @args);
ed81751999-12-11Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  void parse_error (string msg, mixed... args)
d4768a2001-05-18Martin Stjernholm  //! A wrapper for easy access to @[RXML.parse_error].
291ade2000-01-25Martin Stjernholm  {
5ed0132000-02-13Martin Stjernholm  _parse_error (msg, @args);
291ade2000-01-25Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  void tag_debug (string msg, mixed... args)
d4768a2001-05-18Martin Stjernholm  //! Writes the message to the debug log if this tag has //! @[FLAG_DEBUG] set.
ac69772000-09-08Martin Stjernholm  {
2cab1b2001-03-05Martin Nilsson  if (flags & FLAG_DEBUG) report_debug (msg, @args);
ac69772000-09-08Martin Stjernholm  }
c065af2001-07-11Martin Stjernholm  void break_frame (void|Frame|string frame_or_scope) //! Makes the parser break the evaluation up to the specified frame, //! or to the top level if no frame is given. If @[frame_or_scope] //! is a frame object higher up in the stack then the evaluation up //! to and including that frame will be broken, and then continue. //! If @[frame_or_scope] is a string then the closest frame higher //! up in the stack with a scope of that name will be broken. It's //! an error if @[frame_or_scope] is nonzero and doesn't match a //! frame in the stack. Does not return; throws a special exception //! instead. //! //! @note //! It's not well defined how much of the earlier evaluated data //! will be in the final result. Since none of the broken tags are //! executed on the partial data after the break, it won't be //! returned. This means that typically none of the earlier data is //! returned. However if streaming is in use then data in earlier //! returned pieces is not affected, of course.
ed81751999-12-11Martin Stjernholm  {
c065af2001-07-11Martin Stjernholm  throw (BreakEval (frame_or_scope));
ed81751999-12-11Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  void suspend()
d4768a2001-05-18Martin Stjernholm  //! Used together with @[resume] for nonblocking mode. May be called
91c8982000-03-11Martin Stjernholm  //! from any frame callback to suspend the parser: The parser will
d4768a2001-05-18Martin Stjernholm  //! just stop, leaving the context intact. If this function returns, //! the parser is used in a place that doesn't support nonblocking, //! so it's then ok to go ahead and block.
ed81751999-12-11Martin Stjernholm  {
8cb24d2000-02-13Martin Stjernholm  fatal_error ("FIXME\n");
ed81751999-12-11Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  void resume()
ed81751999-12-11Martin Stjernholm  //! Makes the parser continue where it left off. The function that
d4768a2001-05-18Martin Stjernholm  //! called @[suspend] will be called again.
ed81751999-12-11Martin Stjernholm  {
8cb24d2000-02-13Martin Stjernholm  fatal_error ("FIXME\n");
ed81751999-12-11Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  mapping(string:Tag) get_plugins()
cf3c902000-02-04Martin Stjernholm  //! Returns the plugins registered for this tag, which is assumed to
d4768a2001-05-18Martin Stjernholm  //! be a socket tag, i.e. to have @[FLAG_SOCKET_TAG] set (see //! @[Tag.plugin_name] for details). Indices are the //! @tt{plugin_name@} values for the plugin @[RXML.Tag] objects, //! values are the plugin objects themselves. Don't be destructive //! on the returned mapping.
cf3c902000-02-04Martin Stjernholm  { #ifdef MODULE_DEBUG
bca67e2000-10-18Martin Stjernholm  if (!(tag->flags & FLAG_SOCKET_TAG))
8cb24d2000-02-13Martin Stjernholm  fatal_error ("This tag is not a socket tag.\n");
cf3c902000-02-04Martin Stjernholm #endif
be73272001-06-18Martin Stjernholm  return RXML_CONTEXT->tag_set->get_plugins (tag->name, tag->flags & FLAG_PROC_INSTR);
cf3c902000-02-04Martin Stjernholm  }
938bfd2000-11-04Martin Stjernholm  final Tag get_overridden_tag()
d4768a2001-05-18Martin Stjernholm  //! Returns the @[RXML.Tag] object the tag for this frame overrides, //! if any.
641d3c2000-10-12Martin Stjernholm  {
be73272001-06-18Martin Stjernholm  return RXML_CONTEXT->tag_set->get_overridden_tag (tag);
641d3c2000-10-12Martin Stjernholm  }
bca67e2000-10-18Martin Stjernholm  Frame|string propagate_tag (void|mapping(string:string) args, void|string content)
51f88b2000-06-23Martin Stjernholm  //! This function is intended to be used in the execution array from
d4768a2001-05-18Martin Stjernholm  //! @[do_return] etc to propagate the tag to the next overridden tag
51f88b2000-06-23Martin Stjernholm  //! definition, if any exists. It either returns a frame from the //! overridden tag or, if no overridden tag exists, a string //! containing a formatted tag (which requires that the result type
d4768a2001-05-18Martin Stjernholm  //! supports formatted tags, i.e. has a working @[format_tag] //! function). If @[args] and @[content] are given, they will be //! used in the tag after parsing, otherwise the @[raw_tag_text] //! variable is used, which must have a string value.
51f88b2000-06-23Martin Stjernholm  {
4293072001-05-08Martin Stjernholm #ifdef MODULE_DEBUG #define CHECK_RAW_TEXT \
02a6562001-07-09Martin Stjernholm  if (zero_type (this_object()->raw_tag_text)) \
4293072001-05-08Martin Stjernholm  fatal_error ("The variable raw_tag_text must be defined.\n"); \
02a6562001-07-09Martin Stjernholm  if (!stringp (this_object()->raw_tag_text)) \
4293072001-05-08Martin Stjernholm  fatal_error ("raw_tag_text must have a string value.\n"); #else #define CHECK_RAW_TEXT #endif // FIXME: This assumes an xml-like parser.
641d3c2000-10-12Martin Stjernholm  if (object(Tag) overridden = get_overridden_tag()) {
51f88b2000-06-23Martin Stjernholm  Frame frame;
4293072001-05-08Martin Stjernholm  if (flags & FLAG_PROC_INSTR) { if (!content) { CHECK_RAW_TEXT;
02a6562001-07-09Martin Stjernholm  content = t_xml->parse_tag (this_object()->raw_tag_text)[2];
4293072001-05-08Martin Stjernholm #ifdef DEBUG if (!stringp (content)) fatal_error ("Failed to parse PI tag content for <?%s?> from %O.\n",
02a6562001-07-09Martin Stjernholm  tag->name, this_object()->raw_tag_text);
51f88b2000-06-23Martin Stjernholm #endif
4293072001-05-08Martin Stjernholm  } } else if (!args || !content && !(flags & FLAG_EMPTY_ELEMENT)) { CHECK_RAW_TEXT;
d2aca02001-06-11Martin Stjernholm  string ignored;
02a6562001-07-09Martin Stjernholm  [ignored, args, content] = t_xml->parse_tag (this_object()->raw_tag_text);
4c0ed62001-01-18Martin Stjernholm #ifdef DEBUG
4293072001-05-08Martin Stjernholm  if (!mappingp (args)) fatal_error ("Failed to parse tag args for <%s> from %O.\n",
02a6562001-07-09Martin Stjernholm  tag->name, this_object()->raw_tag_text);
4c2b512001-05-17Martin Stjernholm  if (!stringp (content) && !(flags & FLAG_EMPTY_ELEMENT))
4293072001-05-08Martin Stjernholm  fatal_error ("Failed to parse tag content for <%s> from %O.\n",
02a6562001-07-09Martin Stjernholm  tag->name, this_object()->raw_tag_text);
4c0ed62001-01-18Martin Stjernholm #endif
51f88b2000-06-23Martin Stjernholm  } frame = overridden (args, content || ""); frame->flags |= FLAG_UNPARSED; return frame; }
4293072001-05-08Martin Stjernholm  else {
d2aca02001-06-11Martin Stjernholm  CHECK_RAW_TEXT;
6dfc812001-07-20Martin Stjernholm  // Format a new tag, as close to the original as possible.
4293072001-05-08Martin Stjernholm  if (flags & FLAG_PROC_INSTR) {
d2aca02001-06-11Martin Stjernholm  if (content) { string name;
02a6562001-07-09Martin Stjernholm  [name, args, content] = t_xml->parse_tag (this_object()->raw_tag_text);
d2aca02001-06-11Martin Stjernholm  return result_type->format_tag (name, 0, content, tag->flags);
4293072001-05-08Martin Stjernholm  }
d2aca02001-06-11Martin Stjernholm  else
02a6562001-07-09Martin Stjernholm  return this_object()->raw_tag_text;
4293072001-05-08Martin Stjernholm  } else {
d2aca02001-06-11Martin Stjernholm  string s;
9d26462001-06-13Martin Stjernholm  if (!args || !content && !(flags & FLAG_EMPTY_ELEMENT)) {
51f88b2000-06-23Martin Stjernholm #ifdef MODULE_DEBUG
9d26462001-06-13Martin Stjernholm  if (mixed err = catch {
51f88b2000-06-23Martin Stjernholm #endif
02a6562001-07-09Martin Stjernholm  s = t_xml (PXml)->eval (this_object()->raw_tag_text,
be73272001-06-18Martin Stjernholm  RXML_CONTEXT, empty_tag_set);
24f9e82000-08-05Martin Stjernholm #ifdef MODULE_DEBUG
9d26462001-06-13Martin Stjernholm  }) { if (objectp (err) && ([object] err)->thrown_at_unwind) fatal_error ("Can't save parser state when evaluating arguments.\n"); throw_fatal (err); }
24f9e82000-08-05Martin Stjernholm #endif
9d26462001-06-13Martin Stjernholm  if (!args && !content) return s; }
02a6562001-07-09Martin Stjernholm  else s = this_object()->raw_tag_text;
4293072001-05-08Martin Stjernholm 
d2aca02001-06-11Martin Stjernholm  [string name, mapping(string:string) parsed_args,
02a6562001-07-09Martin Stjernholm  string parsed_content] = t_xml->parse_tag (this_object()->raw_tag_text);
4293072001-05-08Martin Stjernholm #ifdef DEBUG
d2aca02001-06-11Martin Stjernholm  if (!mappingp (parsed_args)) fatal_error ("Failed to parse tag args for <%s> from %O.\n",
02a6562001-07-09Martin Stjernholm  tag->name, this_object()->raw_tag_text);
d2aca02001-06-11Martin Stjernholm  if (!stringp (parsed_content)) fatal_error ("Failed to parse tag content for <%s> from %O.\n",
02a6562001-07-09Martin Stjernholm  tag->name, this_object()->raw_tag_text);
4293072001-05-08Martin Stjernholm #endif
d2aca02001-06-11Martin Stjernholm  if (!args) args = parsed_args;
9d26462001-06-13Martin Stjernholm  if (!content && !(flags & FLAG_EMPTY_ELEMENT)) content = parsed_content;
9a64932001-06-12Martin Stjernholm  return result_type->format_tag (name, args, content, tag->flags);
51f88b2000-06-23Martin Stjernholm  }
4293072001-05-08Martin Stjernholm #undef CHECK_RAW_TEXT
d2aca02001-06-11Martin Stjernholm  }
51f88b2000-06-23Martin Stjernholm  }
5c7a6b2001-06-25Martin Stjernholm  // Internals:
ed81751999-12-11Martin Stjernholm 
ac69772000-09-08Martin Stjernholm #ifdef DEBUG
09a16e2001-07-10Martin Stjernholm # define THIS_TAG_TOP_DEBUG(msg, args...) \ (TAG_DEBUG_TEST (flags & FLAG_DEBUG) && \ report_debug ("%O: " + (msg), this_object(), args), 0) # define THIS_TAG_DEBUG(msg, args...) \ (TAG_DEBUG_TEST (flags & FLAG_DEBUG) && \ report_debug ("%O: " + (msg), this_object(), args), 0)
d52f8f2001-05-19Martin Stjernholm # define THIS_TAG_DEBUG_ENTER_SCOPE(ctx, this) \ if (this->vars && ctx->scopes["_"] != this->vars) \ THIS_TAG_DEBUG ("(Re)entering scope %O\n", this->scope_name) # define THIS_TAG_DEBUG_LEAVE_SCOPE(ctx, this) \ if (this->vars && ctx->scopes["_"] == this->vars) \ THIS_TAG_DEBUG ("Leaving scope %O\n", this->scope_name)
ac69772000-09-08Martin Stjernholm #else
4c2b512001-05-17Martin Stjernholm # define THIS_TAG_TOP_DEBUG(msg, args...) 0 # define THIS_TAG_DEBUG(msg, args...) 0
d52f8f2001-05-19Martin Stjernholm # define THIS_TAG_DEBUG_ENTER_SCOPE(ctx, this) 0 # define THIS_TAG_DEBUG_LEAVE_SCOPE(ctx, this) 0
ac69772000-09-08Martin Stjernholm #endif
d52f8f2001-05-19Martin Stjernholm #define SET_SEQUENTIAL(from, to, desc) \ do { \
a835e82001-07-25Martin Stjernholm  THIS_TAG_DEBUG ("Adding %s to " desc "\n", format_short (from)); \
893ac12001-06-21Martin Stjernholm  /* Keep only one ref to to to allow destructive change. */ \ to = to + (to = 0, from); \
d52f8f2001-05-19Martin Stjernholm  } while (0) #define SET_NONSEQUENTIAL(from, to, to_type, desc) \ do { \ if (from != nil) { \ if (to != nil) \ parse_error ( \ "Cannot append another value %s to non-sequential " desc \
a835e82001-07-25Martin Stjernholm  " of type %s.\n", format_short (from), to_type->name); \ THIS_TAG_DEBUG ("Setting " desc " to %s\n", format_short (from)); \
d52f8f2001-05-19Martin Stjernholm  to = from; \ } \ } while (0)
893ac12001-06-21Martin Stjernholm #define CONVERT_VALUE(from, from_type, to, to_type, desc) \
d52f8f2001-05-19Martin Stjernholm  do { \ if (from_type->name != to_type->name) { \
893ac12001-06-21Martin Stjernholm  THIS_TAG_DEBUG (desc, from_type->name, to_type->name); \
d52f8f2001-05-19Martin Stjernholm  to = to_type->encode (from, from_type); \ } \ else to = from; \ } while (0)
893ac12001-06-21Martin Stjernholm #define CONV_RESULT(from, from_type, to, to_type) \ CONVERT_VALUE(from, from_type, to, to_type, \ "Converting result from %s to %s of surrounding content\n")
a08cd72001-06-09Martin Stjernholm  private void _exec_array_fatal (string where, int pos, mixed elem, string msg, mixed... args) { if (sizeof (args)) msg = sprintf (msg, args); fatal_error ("Position %d in exec array from %s is %s: %s", pos, where, elem, msg); }; mixed _exec_array (Context ctx, TagSetParser|PCode evaler, array exec, string where)
ed81751999-12-11Martin Stjernholm  {
02a6562001-07-09Martin Stjernholm  int i = 0;
46c68f2000-08-12Martin Stjernholm  mixed res = nil;
ed81751999-12-11Martin Stjernholm  Parser subparser = 0;
02a6562001-07-09Martin Stjernholm  int orig_make_p_code = ctx->make_p_code;
ed81751999-12-11Martin Stjernholm  mixed err = catch { for (; i < sizeof (exec); i++) {
46c68f2000-08-12Martin Stjernholm  mixed elem = exec[i], piece = nil;
ed81751999-12-11Martin Stjernholm  switch (sprintf ("%t", elem)) { case "string":
7dd3f82001-04-18Martin Stjernholm  if (result_type->parser_prog == PNone) {
a835e82001-07-25Martin Stjernholm  THIS_TAG_DEBUG ("Exec[%d]: String %s\n", i, format_short (elem));
ed81751999-12-11Martin Stjernholm  piece = elem;
ac69772000-09-08Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm  else {
6923692001-07-16Martin Stjernholm  { PCode p_code = 0; if ((ctx->make_p_code = flags & FLAG_COMPILE_RESULT)) { p_code = RenewablePCode (0); p_code->source = [string] elem; } subparser = result_type->get_parser (ctx, ctx->tag_set, evaler, p_code); if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER)) { subparser->recover_errors = 1; if (p_code) p_code->recover_errors = 1; } THIS_TAG_DEBUG ("Exec[%d]: Parsing%s string %s with %O\n", i, p_code ? " and compiling" : "",
a835e82001-07-25Martin Stjernholm  format_short (elem), subparser);
02a6562001-07-09Martin Stjernholm  }
56532d1999-12-19Martin Stjernholm  subparser->finish ([string] elem); // Might unwind. piece = subparser->eval(); // Might unwind.
02a6562001-07-09Martin Stjernholm  if (PCode p_code = subparser->p_code) {
fb605b2001-07-11Martin Stjernholm  // Could perhaps collect adjacent PCode objects here.
02a6562001-07-09Martin Stjernholm  p_code->finish(); exec[i] = p_code; } result_type->give_back (subparser, ctx->tag_set);
ed81751999-12-11Martin Stjernholm  subparser = 0; } break;
ac69772000-09-08Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  case "mapping":
a08cd72001-06-09Martin Stjernholm  THIS_TAG_DEBUG ("Exec[%d]: Response mapping\n", i); _exec_array_fatal (where, i, elem, "Response mappings not yet implemented.\n");
ed81751999-12-11Martin Stjernholm  break;
ac69772000-09-08Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  case "multiset":
ac69772000-09-08Martin Stjernholm  if (sizeof ([multiset] elem) == 1) { piece = ((array) elem)[0];
a835e82001-07-25Martin Stjernholm  THIS_TAG_DEBUG ("Exec[%d]: Verbatim value %s\n", i, format_short (piece));
ac69772000-09-08Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  else _exec_array_fatal (where, i, elem, "Not exactly one value in multiset.\n");
ed81751999-12-11Martin Stjernholm  break;
ac69772000-09-08Martin Stjernholm 
ed81751999-12-11Martin Stjernholm  default:
f7469d2001-07-10Martin Stjernholm  if (objectp (elem)) {
5a455b2000-02-13Martin Stjernholm  // Can't count on that sprintf ("%t", ...) on an object // returns "object". if (([object] elem)->is_RXML_Frame) {
6923692001-07-16Martin Stjernholm  if (orig_make_p_code) // FIXME: Should p-code this if FLAG_COMPILE_RESULT // is set, but then we have to solve the thread // safety and staleness check issues. ctx->make_p_code = 0; THIS_TAG_DEBUG ("Exec[%d]: Evaluating frame %O\n", i, elem);
4c2b512001-05-17Martin Stjernholm  piece = ([object(Frame)] elem)->_eval (
a08cd72001-06-09Martin Stjernholm  ctx, evaler, result_type); // Might unwind.
02a6562001-07-09Martin Stjernholm  ([object(Frame)] elem)->up = 0; // Break potential cyclic reference. break; } else if (([object] elem)->is_RXML_PCode) { THIS_TAG_DEBUG ("Exec[%d]: Evaluating p-code %O\n", i, elem); piece = ([object(PCode)] elem)->_eval (ctx); CONVERT_VALUE (piece, ([object(PCode)] elem)->type, piece, result_type, "Converting p-code result from %s " "to tag result type %s\n");
a08cd72001-06-09Martin Stjernholm  break;
5a455b2000-02-13Martin Stjernholm  } else if (([object] elem)->is_RXML_Parser) { // The subparser above unwound.
02a6562001-07-09Martin Stjernholm  THIS_TAG_DEBUG ("Exec[%d]: Continuing eval of frame %O\n", i, elem);
5a455b2000-02-13Martin Stjernholm  ([object(Parser)] elem)->finish(); // Might unwind. piece = ([object(Parser)] elem)->eval(); // Might unwind.
a08cd72001-06-09Martin Stjernholm  break;
5a455b2000-02-13Martin Stjernholm  }
f7469d2001-07-10Martin Stjernholm  else if (elem == nil) break; } else if (functionp (elem)) { THIS_TAG_DEBUG ("Exec[%d]: Calling function %O\n", i, elem); piece = ([function(RequestID:mixed)] elem) (ctx->id); // Might unwind. break; }
a08cd72001-06-09Martin Stjernholm  _exec_array_fatal (where, i, elem, "Not a valid type.\n");
ed81751999-12-11Martin Stjernholm  }
d52f8f2001-05-19Martin Stjernholm  if (result_type->sequential) SET_SEQUENTIAL (piece, res, "result"); else SET_NONSEQUENTIAL (piece, result, result_type, "result");
ed81751999-12-11Martin Stjernholm  }
893ac12001-06-21Martin Stjernholm  if (result_type->sequential) result = result + (result = 0, res);
d52f8f2001-05-19Martin Stjernholm  else res = result;
02a6562001-07-09Martin Stjernholm  ctx->make_p_code = orig_make_p_code;
ed81751999-12-11Martin Stjernholm  return res; };
893ac12001-06-21Martin Stjernholm  if (result_type->sequential) result = result + (result = 0, res);
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  ctx->make_p_code = orig_make_p_code;
56532d1999-12-19Martin Stjernholm  if (objectp (err) && ([object] err)->thrown_at_unwind) {
4c2b512001-05-17Martin Stjernholm  THIS_TAG_DEBUG ("Exec: Interrupted at position %d\n", i);
d4768a2001-05-18Martin Stjernholm  UNWIND_STATE ustate;
33712a2000-03-13Martin Stjernholm  if ((ustate = ctx->unwind_state) && !zero_type (ustate->stream_piece)) {
ed81751999-12-11Martin Stjernholm  // Subframe wants to stream. Update stream_piece and send it on.
d52f8f2001-05-19Martin Stjernholm  if (result_type->name != evaler->type->name) res = evaler->type->encode (res, result_type);
ed81751999-12-11Martin Stjernholm  if (result_type->sequential)
d52f8f2001-05-19Martin Stjernholm  SET_SEQUENTIAL (ustate->stream_piece, res, "stream piece"); else SET_NONSEQUENTIAL (ustate->stream_piece, res, result_type, "stream piece"); ustate->stream_piece = res;
33712a2000-03-13Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm  ustate->exec_left = exec[i..]; // Left to execute. if (subparser) // Replace the string with the subparser object so that we'll
56532d1999-12-19Martin Stjernholm  // continue in it later. It's done here to keep the original // exec array untouched. ([array] ustate->exec_left)[0] = subparser;
8cb24d2000-02-13Martin Stjernholm  throw (err);
ed81751999-12-11Martin Stjernholm  }
8cb24d2000-02-13Martin Stjernholm  throw_fatal (err);
ed81751999-12-11Martin Stjernholm  }
be73272001-06-18Martin Stjernholm  private void _handle_runtime_tags (Context ctx, TagSetParser|PCode evaler)
2bd21a2000-01-05Martin Stjernholm  {
be73272001-06-18Martin Stjernholm  if (evaler->is_RXML_Parser) { array(Tag) arr_add_tags = ctx->new_runtime_tags->added_tags(); array(string) arr_rem_tags = ctx->new_runtime_tags->removed_tags(); array(string) arr_rem_pi_tags = ctx->new_runtime_tags->removed_pi_tags(); for (Parser p = evaler; p; p = p->_parent) if (p->tag_set_eval && !p->_local_tag_set && p->add_runtime_tag) { foreach (arr_add_tags, Tag tag) { THIS_TAG_DEBUG ("Adding runtime tag %O\n", tag); ([object(TagSetParser)] p)->add_runtime_tag (tag); } foreach (arr_rem_tags, string tag) { THIS_TAG_DEBUG ("Removing runtime tag %s\n", tag); ([object(TagSetParser)] p)->remove_runtime_tag (tag); } foreach (arr_rem_pi_tags, string tag) { THIS_TAG_DEBUG ("Removing runtime tag %s\n", tag); ([object(TagSetParser)] p)->remove_runtime_tag (tag, 1); }
ac69772000-09-08Martin Stjernholm  }
be73272001-06-18Martin Stjernholm  } // FIXME: When the evaler is a PCode object we should have a debug // check here that ensures that the same runtime tag changes are // done as in the first eval.
24f9e82000-08-05Martin Stjernholm  ctx->runtime_tags = ctx->new_runtime_tags->filter_tags (ctx->runtime_tags);
0ebc9d2000-02-15Martin Stjernholm  ctx->new_runtime_tags = 0;
2bd21a2000-01-05Martin Stjernholm  }
d52f8f2001-05-19Martin Stjernholm #define LOW_CALL_CALLBACK(res, cb, args...) \ do { \ THIS_TAG_DEBUG ("Calling " #cb "\n"); \ COND_PROF_ENTER(tag,tag->name,"tag"); \
a08cd72001-06-09Martin Stjernholm  res = (cb) (args); /* Might unwind. */ \
d52f8f2001-05-19Martin Stjernholm  COND_PROF_LEAVE(tag,tag->name,"tag"); \ } while (0) #define EXEC_CALLBACK(ctx, evaler, exec, cb, args...) \ do { \ if (!exec) \ if (arrayp (cb)) { \ THIS_TAG_DEBUG ("Getting exec array from " #cb "\n"); \ exec = [array] cb; \ } \ else { \ LOW_CALL_CALLBACK (exec, cb, args); \ THIS_TAG_DEBUG ((exec ? "Exec array of length " + \ sizeof (exec) : "Zero") + \ " returned from " #cb "\n"); \
02a6562001-07-09Martin Stjernholm  THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this_object()); \ ENTER_SCOPE (ctx, this_object()); \
be73272001-06-18Martin Stjernholm  if (ctx->new_runtime_tags) \
d52f8f2001-05-19Martin Stjernholm  _handle_runtime_tags (ctx, evaler); \ } \ } while (0) #define EXEC_ARRAY(ctx, evaler, exec, cb) \ do { \ if (exec) { \
a08cd72001-06-09Martin Stjernholm  mixed res = \ _exec_array (ctx, evaler, exec, #cb); /* Might unwind. */ \
d52f8f2001-05-19Martin Stjernholm  if (flags & FLAG_STREAM_RESULT) { \ DO_IF_DEBUG ( \ if (ctx->unwind_state) \ fatal_error ("Clobbering unwind_state to do streaming.\n"); \ if (piece != nil) \ fatal_error ("Thanks, we think about how nice it must be " \ "to play the harmonica...\n"); \ ); \ CONV_RESULT (res, result_type, res, type); \
bce4d22001-06-19Martin Stjernholm  ctx->unwind_state = (["stream_piece": res, \ "reason": "streaming"]); \
d52f8f2001-05-19Martin Stjernholm  THIS_TAG_DEBUG ("Streaming %s from " #cb "\n", \
a835e82001-07-25Martin Stjernholm  format_short (res)); \
02a6562001-07-09Martin Stjernholm  throw (this_object()); \
d52f8f2001-05-19Martin Stjernholm  } \ exec = 0; \ } \ } while (0)
80e42c2001-06-26Martin Stjernholm  EVAL_ARGS_FUNC|string _prepare (Context ctx, Type type, mapping(string:string) raw_args, PCode p_code)
a08cd72001-06-09Martin Stjernholm  // Evaluates raw_args simultaneously as generating the // EVAL_ARGS_FUNC function. The result of the evaluations is stored // in args. Might be destructive on raw_args. { mixed err = catch {
6dfc812001-07-20Martin Stjernholm  if (ctx->frame_depth >= Context.max_frame_depth)
a08cd72001-06-09Martin Stjernholm  _run_error ("Too deep recursion -- exceeding %d nested tags.\n",
6dfc812001-07-20Martin Stjernholm  Context.max_frame_depth);
a08cd72001-06-09Martin Stjernholm 
80e42c2001-06-26Martin Stjernholm  EVAL_ARGS_FUNC|string func;
a08cd72001-06-09Martin Stjernholm  if (raw_args) { #ifdef MODULE_DEBUG if (flags & FLAG_PROC_INSTR) fatal_error ("Can't pass arguments to a processing instruction tag.\n"); #endif #ifdef MAGIC_HELP_ARG if (raw_args->help) { func = utils->return_help_arg; args = raw_args; } else #endif if (sizeof (raw_args)) {
a529ca2001-07-11Martin Stjernholm  // Note: Approximate code duplication in Tag.eval_args and // Tag._eval_splice_args.
a08cd72001-06-09Martin Stjernholm  string splice_arg = raw_args["::"];
d2aca02001-06-11Martin Stjernholm  if (splice_arg) m_delete (raw_args, "::");
a08cd72001-06-09Martin Stjernholm  else splice_arg = 0; mapping(string:Type) splice_req_types; mapping(string:Type) atypes = raw_args & tag->req_arg_types; if (sizeof (atypes) < sizeof (tag->req_arg_types)) if (splice_arg) splice_req_types = tag->req_arg_types - atypes; else { array(string) missing = sort (indices (tag->req_arg_types - atypes)); parse_error ("Required " + (sizeof (missing) > 1 ? "arguments " + String.implode_nicely (missing) + " are" : "argument " + missing[0] + " is") + " missing.\n"); } atypes += raw_args & tag->opt_arg_types;
02a6562001-07-09Martin Stjernholm  String.Buffer fn_text; function(string...:void) fn_text_add;
fb605b2001-07-11Martin Stjernholm  PCode sub_p_code = 0; PikeCompile comp;
02a6562001-07-09Martin Stjernholm  if (p_code) { fn_text_add = (fn_text = String.Buffer())->add; fn_text_add ("mixed tmp;\n");
fb605b2001-07-11Martin Stjernholm  sub_p_code = PCode (0); comp = ctx->p_code_comp;
02a6562001-07-09Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  if (splice_arg) {
d2aca02001-06-11Martin Stjernholm  // Note: This assumes an XML-like parser.
fb605b2001-07-11Martin Stjernholm  Parser parser = splice_arg_type->get_parser (ctx, ctx->tag_set, 0, sub_p_code);
a08cd72001-06-09Martin Stjernholm  THIS_TAG_DEBUG ("Evaluating splice argument %s\n",
a835e82001-07-25Martin Stjernholm  format_short (splice_arg));
a08cd72001-06-09Martin Stjernholm #ifdef MODULE_DEBUG if (mixed err = catch { #endif
02a6562001-07-09Martin Stjernholm  parser->finish (splice_arg); // Should not unwind. splice_arg = parser->eval(); // Should not unwind.
a08cd72001-06-09Martin Stjernholm #ifdef MODULE_DEBUG }) { if (objectp (err) && ([object] err)->thrown_at_unwind) fatal_error ("Can't save parser state when " "evaluating splice argument.\n"); throw_fatal (err); } #endif
80e42c2001-06-26Martin Stjernholm  if (p_code)
02a6562001-07-09Martin Stjernholm  fn_text_add (
fb605b2001-07-11Martin Stjernholm  "return ", comp->bind (tag->_eval_splice_args), "(ctx,", comp->bind (xml_tag_parser->parse_tag_args), "((", sub_p_code->compile_text (comp), ")||\"\"),", comp->bind (splice_req_types), ")+([\n");
02a6562001-07-09Martin Stjernholm  splice_arg_type->give_back (parser, ctx->tag_set);
a529ca2001-07-11Martin Stjernholm  args = tag->_eval_splice_args ( ctx, xml_tag_parser->parse_tag_args (splice_arg || ""), splice_req_types);
a08cd72001-06-09Martin Stjernholm  } else { args = raw_args;
02a6562001-07-09Martin Stjernholm  if (p_code) fn_text_add ("return ([\n");
a08cd72001-06-09Martin Stjernholm  } #ifdef MODULE_DEBUG if (mixed err = catch { #endif
02a6562001-07-09Martin Stjernholm  TagSet ctx_tag_set = ctx->tag_set;
80e42c2001-06-26Martin Stjernholm  if (p_code) foreach (indices (raw_args), string arg) { Type t = atypes[arg] || tag->def_arg_type; if (t->parser_prog != PNone) {
fb605b2001-07-11Martin Stjernholm  Parser parser = t->get_parser (ctx, ctx_tag_set, 0, sub_p_code);
80e42c2001-06-26Martin Stjernholm  THIS_TAG_DEBUG ("Evaluating and compiling " "argument value %s with %O\n",
a835e82001-07-25Martin Stjernholm  format_short (raw_args[arg]), parser);
80e42c2001-06-26Martin Stjernholm  parser->finish (raw_args[arg]); // Should not unwind. args[arg] = parser->eval(); // Should not unwind. THIS_TAG_DEBUG ("Setting argument %s to %s\n",
a835e82001-07-25Martin Stjernholm  format_short (arg), format_short (args[arg]));
02a6562001-07-09Martin Stjernholm  fn_text_add (sprintf ("%O: %s,\n", arg,
fb605b2001-07-11Martin Stjernholm  sub_p_code->compile_text (comp)));
02a6562001-07-09Martin Stjernholm  t->give_back (parser, ctx_tag_set);
80e42c2001-06-26Martin Stjernholm  } else { args[arg] = raw_args[arg];
fb605b2001-07-11Martin Stjernholm  fn_text_add (sprintf ("%O: %s,\n", arg, comp->bind (raw_args[arg])));
80e42c2001-06-26Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  }
80e42c2001-06-26Martin Stjernholm  else foreach (indices (raw_args), string arg) { Type t = atypes[arg] || tag->def_arg_type; if (t->parser_prog != PNone) {
02a6562001-07-09Martin Stjernholm  Parser parser = t->get_parser (ctx, ctx_tag_set, 0, 0);
80e42c2001-06-26Martin Stjernholm  THIS_TAG_DEBUG ("Evaluating argument value %s with %O\n",
a835e82001-07-25Martin Stjernholm  format_short (raw_args[arg]), parser);
80e42c2001-06-26Martin Stjernholm  parser->finish (raw_args[arg]); // Should not unwind. args[arg] = parser->eval(); // Should not unwind. THIS_TAG_DEBUG ("Setting argument %s to %s\n",
a835e82001-07-25Martin Stjernholm  format_short (arg), format_short (args[arg]));
02a6562001-07-09Martin Stjernholm  t->give_back (parser, ctx_tag_set);
80e42c2001-06-26Martin Stjernholm  } else args[arg] = raw_args[arg];
a08cd72001-06-09Martin Stjernholm  } #ifdef MODULE_DEBUG }) { if (objectp (err) && ([object] err)->thrown_at_unwind) fatal_error ("Can't save parser state when evaluating arguments.\n"); throw_fatal (err); } #endif
80e42c2001-06-26Martin Stjernholm  if (p_code) {
02a6562001-07-09Martin Stjernholm  fn_text_add ("]);\n");
fb605b2001-07-11Martin Stjernholm  func = ctx->p_code_comp->add_func (
02a6562001-07-09Martin Stjernholm  "mapping(string:mixed)", "object ctx, object evaler", fn_text->get());
80e42c2001-06-26Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  } else { func = utils->return_empty_mapping; args = raw_args; } } else func = utils->return_zero; if (!result_type) { #ifdef MODULE_DEBUG if (!tag) fatal_error ("result_type not set in Frame object %O, " "and it has no Tag object to use for inferring it.\n",
02a6562001-07-09Martin Stjernholm  this_object());
a08cd72001-06-09Martin Stjernholm #endif find_result_type: { // First check if any of the types is a subtype of the // wanted type. If so, we can use it directly. foreach (tag->result_types, Type rtype) if (rtype->subtype_of (type)) { result_type = rtype; break find_result_type; } // Then check if any of the types is a supertype of the // wanted type. If so, set the result type to the wanted // type, since the tag has the responsibility to produce a // value of that type. foreach (tag->result_types, Type rtype) if (type->subtype_of (rtype)) { result_type = type (rtype->parser_prog, @rtype->parser_args); break find_result_type; } parse_error ( "Tag returns %s but %s is expected.\n", String.implode_nicely ([array(string)] tag->result_types->name, "or"), type->name); } THIS_TAG_DEBUG ("Resolved result_type to %s from surrounding %s\n", result_type->name, type->name); } else THIS_TAG_DEBUG ("Keeping result_type %s\n", result_type->name); if (!content_type) { #ifdef MODULE_DEBUG if (!tag) fatal_error ("content_type not set in Frame object %O, " "and it has no Tag object to use for inferring it.\n",
02a6562001-07-09Martin Stjernholm  this_object());
a08cd72001-06-09Martin Stjernholm #endif content_type = tag->content_type; if (content_type == t_same) { content_type = result_type (content_type->parser_prog, @content_type->parser_args);
a835e82001-07-25Martin Stjernholm  THIS_TAG_DEBUG ("Resolved t_same to content_type %s\n", content_type->name);
a08cd72001-06-09Martin Stjernholm  }
a835e82001-07-25Martin Stjernholm  else THIS_TAG_DEBUG ("Setting content_type to %s from tag\n", content_type->name);
a08cd72001-06-09Martin Stjernholm  }
a835e82001-07-25Martin Stjernholm  else THIS_TAG_DEBUG ("Keeping content_type %s\n", content_type->name);
a08cd72001-06-09Martin Stjernholm  return func; }; throw (err); }
02a6562001-07-09Martin Stjernholm  mixed _eval (Context ctx, TagSetParser|PCode evaler, Type type)
d52f8f2001-05-19Martin Stjernholm  // Note: It might be somewhat tricky to override this function,
80e42c2001-06-26Martin Stjernholm  // since it handles unwinding and rewinding.
ed81751999-12-11Martin Stjernholm  {
b483382000-06-30Martin Stjernholm  RequestID id = ctx->id;
ed81751999-12-11Martin Stjernholm 
c6245b1999-12-31Martin Stjernholm  // Unwind state data:
02a6562001-07-09Martin Stjernholm #define EVSTAT_NONE 0 #define EVSTAT_BEGIN 1 #define EVSTAT_ENTERED 2 #define EVSTAT_LAST_ITER 3 #define EVSTAT_ITER_DONE 4 int eval_state = EVSTAT_NONE; EVAL_ARGS_FUNC in_args = 0; string|PCode in_content = 0;
291ade2000-01-25Martin Stjernholm  int iter;
ac69772000-09-08Martin Stjernholm #ifdef DEBUG int debug_iter = 1; #endif
a08cd72001-06-09Martin Stjernholm  object(Parser)|object(PCode) subevaler;
6dfc812001-07-20Martin Stjernholm  mixed piece;
d52f8f2001-05-19Martin Stjernholm  array exec = 0;
f02d002000-03-18Martin Stjernholm  TagSet orig_tag_set; // Flags that we added additional_tags to ctx->tag_set.
c6245b1999-12-31Martin Stjernholm  //ctx->new_runtime_tags
02a6562001-07-09Martin Stjernholm  int orig_make_p_code;
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm #define PRE_INIT_ERROR(X...) (ctx->frame = this_object(), fatal_error (X))
2bd21a2000-01-05Martin Stjernholm #ifdef DEBUG // Internal sanity checks.
be73272001-06-18Martin Stjernholm  if (ctx != RXML_CONTEXT)
d52f8f2001-05-19Martin Stjernholm  PRE_INIT_ERROR ("Context not current.\n"); if (!evaler->tag_set_eval) PRE_INIT_ERROR ("Calling _eval() with non-tag set parser.\n");
deb79a2001-06-26Martin Stjernholm  if (up)
02a6562001-07-09Martin Stjernholm  PRE_INIT_ERROR ("Up frame already set. Frame reused in different context?\n");
f9e8b12001-08-09Martin Stjernholm  if (id && ctx->misc != ctx->id->misc->defines) PRE_INIT_ERROR ("ctx->misc != ctx->id->misc->defines\n");
2bd21a2000-01-05Martin Stjernholm #endif #ifdef MODULE_DEBUG if (ctx->new_runtime_tags) PRE_INIT_ERROR ("Looks like Context.add_runtime_tag() or " "Context.remove_runtime_tag() was used outside any parser.\n"); #endif
ed81751999-12-11Martin Stjernholm #undef PRE_INIT_ERROR
a08cd72001-06-09Martin Stjernholm 
80e42c2001-06-26Martin Stjernholm  up = ctx->frame;
02a6562001-07-09Martin Stjernholm  ctx->frame = this_object();
446bfa2001-06-21Martin Stjernholm  ctx->frame_depth++;
f853b82001-07-19Martin Stjernholm  FRAME_DEPTH_MSG ("%*s%O frame_depth increase line %d\n", ctx->frame_depth, "", this_object(), __LINE__);
446bfa2001-06-21Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  process_tag:
02a6562001-07-09Martin Stjernholm  while (1) { // Looping only when continuing in streaming mode. if (mixed err = catch { // Catch errors but don't allow unwinds. if (array state = ctx->unwind_state && ctx->unwind_state[this_object()]) {
a08cd72001-06-09Martin Stjernholm  object ignored; [ignored, eval_state, in_args, in_content, iter,
02a6562001-07-09Martin Stjernholm  subevaler, piece, exec, orig_tag_set, ctx->new_runtime_tags, orig_make_p_code
a08cd72001-06-09Martin Stjernholm #ifdef DEBUG , debug_iter #endif ] = state;
02a6562001-07-09Martin Stjernholm  m_delete (ctx->unwind_state, this_object());
a08cd72001-06-09Martin Stjernholm  if (!sizeof (ctx->unwind_state)) ctx->unwind_state = 0;
fb605b2001-07-11Martin Stjernholm  ctx->make_p_code = orig_make_p_code;
a08cd72001-06-09Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Continuing evaluation" + (piece ? " with stream piece\n" : "\n"));
7dd3f82001-04-18Martin Stjernholm  }
b483382000-06-30Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  else { // Initialize a new evaluation. if (tag) { TRACE_ENTER("tag &lt;" + tag->name + "&gt;", tag); #ifdef MODULE_LEVEL_SECURITY if (id->conf->check_security (tag, id, id->misc->seclevel)) { THIS_TAG_TOP_DEBUG ("Access denied - exiting\n"); TRACE_LEAVE("access denied");
8d92582001-07-26Martin Stjernholm  return result = nil;
7dd3f82001-04-18Martin Stjernholm  }
ac69772000-09-08Martin Stjernholm #endif
a08cd72001-06-09Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  orig_make_p_code = ctx->make_p_code; if (functionp (args)) {
80e42c2001-06-26Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Evaluating with compiled arguments\n");
02a6562001-07-09Martin Stjernholm  args = (in_args = [EVAL_ARGS_FUNC] args) (ctx, evaler); in_content = content;
80e42c2001-06-26Martin Stjernholm  content = nil; } else if (flags & FLAG_UNPARSED) {
a08cd72001-06-09Martin Stjernholm #ifdef DEBUG
02a6562001-07-09Martin Stjernholm  if (!(flags & FLAG_PROC_INSTR) && !mappingp (args)) fatal_error ("args is not a mapping in unparsed frame: %O\n", args); if (content && !stringp (content)) fatal_error ("content is not a string in unparsed frame: %O.\n", content);
ed81751999-12-11Martin Stjernholm #endif
02a6562001-07-09Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Evaluating%s unparsed\n", ctx->make_p_code ? " and compiling" : ""); if (PCode p_code = ctx->make_p_code &&
6dfc812001-07-20Martin Stjernholm  (evaler->is_RXML_PCode ? evaler : evaler->p_code)) {
02a6562001-07-09Martin Stjernholm  // FIXME: Perhaps make a new PCode object if the evaler // doesn't provide one?
6dfc812001-07-20Martin Stjernholm  if (!ctx->p_code_comp) ctx->p_code_comp = PikeCompile();
02a6562001-07-09Martin Stjernholm  in_args = _prepare (ctx, type, args && args + ([]), p_code);
9686722001-07-25Martin Stjernholm  ctx->state_updated++;
6dfc812001-07-20Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  else _prepare (ctx, type, args && args + ([]), 0);
a08cd72001-06-09Martin Stjernholm  in_content = content;
7dd3f82001-04-18Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  else { THIS_TAG_TOP_DEBUG ("Evaluating with constant arguments and content\n");
80e42c2001-06-26Martin Stjernholm  _prepare (ctx, type, 0, 0);
d4768a2001-05-18Martin Stjernholm  }
689dc62000-08-31Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  result = piece = nil;
02a6562001-07-09Martin Stjernholm  eval_state = EVSTAT_BEGIN;
87fb7e2000-03-25Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  if (TagSet add_tags = [object(TagSet)] this_object()->additional_tags) {
be73272001-06-18Martin Stjernholm  TagSet tset = ctx->tag_set; if (!tset->has_effective_tags (add_tags)) { THIS_TAG_DEBUG ("Installing additional_tags %O\n", add_tags); orig_tag_set = tset;
02a6562001-07-09Martin Stjernholm  TagSet comp_ts; GET_COMPOSITE_TAG_SET (add_tags, tset, comp_ts); ctx->tag_set = comp_ts;
be73272001-06-18Martin Stjernholm  } else THIS_TAG_DEBUG ("Not installing additional_tags %O " "since they're already in the tag set\n", add_tags); }
02a6562001-07-09Martin Stjernholm  if (!zero_type (this_object()->parent_frame)) // Note: This could be done in _prepare, but then we'd have // to fix some sort of frame addressing when saving the // frame state. if (up->local_tags && up->local_tags->has_tag (tag)) { THIS_TAG_DEBUG ("Setting parent_frame to %O from local_tags\n", up); this_object()->parent_frame = up; } else { int nest = 1; Frame frame = up; for (; frame; frame = frame->up) if (frame->additional_tags && frame->additional_tags->has_tag (tag)) { if (!--nest) break; } else if (frame->tag == tag) nest++; THIS_TAG_DEBUG ("Setting parent_frame to %O from additional_tags\n", frame); this_object()->parent_frame = frame; }
ac69772000-09-08Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm #ifdef MAGIC_HELP_ARG if (tag && (args || ([]))->help) { TRACE_ENTER ("tag &lt;" + tag->name + " help&gt;", tag); string help = id->conf->find_tag_doc (tag->name, id); TRACE_LEAVE (""); THIS_TAG_TOP_DEBUG ("Reporting help - frame done\n"); ctx->handle_exception ( // Will throw if necessary. Backtrace ("help", help, ctx), evaler);
8d92582001-07-26Martin Stjernholm  return result = nil;
7dd3f82001-04-18Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm #endif
b483382000-06-30Martin Stjernholm  switch (eval_state) { case EVSTAT_BEGIN: if (array|function(RequestID:array) do_enter =
02a6562001-07-09Martin Stjernholm  [array|function(RequestID:array)] this_object()->do_enter) {
d52f8f2001-05-19Martin Stjernholm  EXEC_CALLBACK (ctx, evaler, exec, do_enter, id); EXEC_ARRAY (ctx, evaler, exec, do_enter); } else {
02a6562001-07-09Martin Stjernholm  THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this_object()); ENTER_SCOPE (ctx, this_object());
291ade2000-01-25Martin Stjernholm  }
6dfc812001-07-20Martin Stjernholm  if (flags & FLAG_UNPARSED) content = nil;
b483382000-06-30Martin Stjernholm  eval_state = EVSTAT_ENTERED;
6dfc812001-07-20Martin Stjernholm  // Fall through.
4c2b512001-05-17Martin Stjernholm 
b483382000-06-30Martin Stjernholm  case EVSTAT_ENTERED: case EVSTAT_LAST_ITER:
d52f8f2001-05-19Martin Stjernholm  int|function(RequestID:int) do_iterate =
02a6562001-07-09Martin Stjernholm  [int|function(RequestID:int)] this_object()->do_iterate;
d52f8f2001-05-19Martin Stjernholm  array|function(RequestID:array) do_process =
02a6562001-07-09Martin Stjernholm  [array|function(RequestID:array)] this_object()->do_process; int finished = 0;
d52f8f2001-05-19Martin Stjernholm 
b483382000-06-30Martin Stjernholm  do { if (eval_state != EVSTAT_LAST_ITER) { if (intp (do_iterate)) { iter = [int] do_iterate || 1; eval_state = EVSTAT_LAST_ITER;
ac69772000-09-08Martin Stjernholm #ifdef DEBUG if (iter > 1)
4c2b512001-05-17Martin Stjernholm  THIS_TAG_DEBUG ("Getting %d iterations from do_iterate\n", iter);
ac69772000-09-08Martin Stjernholm  else if (iter < 0) THIS_TAG_DEBUG ("Skipping to finish since do_iterate is negative\n"); #endif
b483382000-06-30Martin Stjernholm  } else {
d52f8f2001-05-19Martin Stjernholm  LOW_CALL_CALLBACK (iter, do_iterate, id);
4c2b512001-05-17Martin Stjernholm  THIS_TAG_DEBUG ("%O returned from do_iterate\n", iter);
02a6562001-07-09Martin Stjernholm  THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this_object()); ENTER_SCOPE (ctx, this_object());
be73272001-06-18Martin Stjernholm  if (ctx->new_runtime_tags)
d52f8f2001-05-19Martin Stjernholm  _handle_runtime_tags (ctx, evaler);
deecca2001-06-28Martin Stjernholm  if (iter <= 0) eval_state = EVSTAT_LAST_ITER;
b483382000-06-30Martin Stjernholm  }
0ebc9d2000-02-15Martin Stjernholm  }
ac69772000-09-08Martin Stjernholm 
d52f8f2001-05-19Martin Stjernholm  for (; iter > 0; iter-- DO_IF_DEBUG (, debug_iter++)) {
80e42c2001-06-26Martin Stjernholm  eval_content:
02a6562001-07-09Martin Stjernholm  { finished++;
a08cd72001-06-09Martin Stjernholm  if (subevaler) finished = 0; // Continuing an unwound subevaler.
6dfc812001-07-20Martin Stjernholm  else if (!in_content || in_content == "") { if (flags & FLAG_GET_EVALED_CONTENT) { this_object()->evaled_content = PCode (content_type); this_object()->evaled_content->finish(); }
02a6562001-07-09Martin Stjernholm  break eval_content; // No content to handle.
6dfc812001-07-20Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  else if (stringp (in_content)) {
02a6562001-07-09Martin Stjernholm  if (flags & FLAG_EMPTY_ELEMENT)
a08cd72001-06-09Martin Stjernholm  parse_error ("This tag doesn't handle content.\n"); else { // The nested content is not yet parsed.
02a6562001-07-09Martin Stjernholm  if (finished > 1) // Looped once. Always compile since it's // likely we'll loop again.
6dfc812001-07-20Martin Stjernholm  ctx->make_p_code = 1; { PCode p_code = ctx->make_p_code && PCode (0); if (flags & FLAG_GET_EVALED_CONTENT) { PCode evaled_content = this_object()->evaled_content = PCode (0, 0, ctx); if (p_code) p_code->p_code = evaled_content; else p_code = evaled_content; } if (this_object()->local_tags) { subevaler = content_type->get_parser ( ctx, [object(TagSet)] this_object()->local_tags, evaler, p_code); subevaler->_local_tag_set = 1;
cf042f2001-07-21Martin Stjernholm  THIS_TAG_DEBUG ("Iter[%d]: Parsing%s%s content %s "
6dfc812001-07-20Martin Stjernholm  "with %O from local_tags\n", debug_iter,
fb605b2001-07-11Martin Stjernholm  ctx->make_p_code ? " and compiling" : "",
cf042f2001-07-21Martin Stjernholm  flags & FLAG_GET_EVALED_CONTENT ? " and result compiling" : "",
a835e82001-07-25Martin Stjernholm  format_short (in_content), subevaler);
6dfc812001-07-20Martin Stjernholm  } else { subevaler = content_type->get_parser ( ctx, ctx->tag_set, evaler, p_code);
cf042f2001-07-21Martin Stjernholm  THIS_TAG_DEBUG ("Iter[%d]: Parsing%s%s content %s " "with %O%s\n", debug_iter, ctx->make_p_code ? " and compiling" : "", flags & FLAG_GET_EVALED_CONTENT ? " and result compiling" : "",
a835e82001-07-25Martin Stjernholm  format_short (in_content), subevaler,
cf042f2001-07-21Martin Stjernholm  this_object()->additional_tags ? " from additional_tags" : "");
6dfc812001-07-20Martin Stjernholm  } if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER)) { subevaler->recover_errors = 1; if (p_code) { p_code->recover_errors = 1; if ((p_code = p_code->p_code)) p_code->recover_errors = 1; } }
446bfa2001-06-21Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  subevaler->finish (in_content); // Might unwind.
0f998b2000-08-15Martin Stjernholm  finished = 1;
b483382000-06-30Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  } else {
fb605b2001-07-11Martin Stjernholm  subevaler = in_content;
6dfc812001-07-20Martin Stjernholm  if (flags & FLAG_GET_EVALED_CONTENT) subevaler->p_code = this_object()->evaled_content = PCode (content_type, subevaler->tag_set, ctx);
cf042f2001-07-21Martin Stjernholm  THIS_TAG_DEBUG ("Iter[%d]: Evaluating%s with compiled content\n", debug_iter, flags & FLAG_GET_EVALED_CONTENT ? " and result compiling" : "");
a08cd72001-06-09Martin Stjernholm  }
ed81751999-12-11Martin Stjernholm 
a08cd72001-06-09Martin Stjernholm  eval_sub: do { if (piece != nil && flags & FLAG_STREAM_CONTENT) { // Handle a stream piece. THIS_TAG_DEBUG ("Iter[%d]: Got %s stream piece %s\n", debug_iter, finished ? "ending" : "a",
a835e82001-07-25Martin Stjernholm  format_short (piece));
a08cd72001-06-09Martin Stjernholm  if (!arrayp (do_process)) { EXEC_CALLBACK (ctx, evaler, exec, do_process, id, piece); if (exec) { mixed res = _exec_array ( ctx, evaler, exec, "do_process"); // Might unwind. if (flags & FLAG_STREAM_RESULT) {
ed81751999-12-11Martin Stjernholm #ifdef DEBUG
a08cd72001-06-09Martin Stjernholm  if (!zero_type (ctx->unwind_state->stream_piece)) fatal_error ("Clobbering unwind_state->stream_piece.\n");
ed81751999-12-11Martin Stjernholm #endif
a08cd72001-06-09Martin Stjernholm  CONV_RESULT (res, result_type, res, type); ctx->unwind_state->stream_piece = res;
bce4d22001-06-19Martin Stjernholm  ctx->unwind_state->reason = "streaming";
a08cd72001-06-09Martin Stjernholm  THIS_TAG_DEBUG ("Iter[%d]: Streaming %s from do_process\n",
a835e82001-07-25Martin Stjernholm  debug_iter, format_short (res));
02a6562001-07-09Martin Stjernholm  throw (this_object());
49897a2000-02-13Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  exec = 0;
49897a2000-02-13Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  else if (flags & FLAG_STREAM_RESULT) { THIS_TAG_DEBUG ("Iter[%d]: do_process finished the stream; " "ignoring remaining content\n", debug_iter); ctx->unwind_state = 0; piece = nil; break eval_sub;
0f998b2000-08-15Martin Stjernholm  }
b483382000-06-30Martin Stjernholm  }
a08cd72001-06-09Martin Stjernholm  piece = nil; if (finished) break eval_sub; } else { // No streaming. piece = nil; if (finished) { mixed res = subevaler->_eval (ctx); // Might unwind. if (content_type->sequential) SET_SEQUENTIAL (res, content, "content"); else if (res != nil) SET_NONSEQUENTIAL (res, content, content_type, "content"); break eval_sub; } }
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  subevaler->finish(); // Might unwind.
a08cd72001-06-09Martin Stjernholm  finished = 1; } while (1); // Only loops when an unwound subevaler has been recovered.
02a6562001-07-09Martin Stjernholm  if (PCode p_code = subevaler->p_code) {
cf042f2001-07-21Martin Stjernholm  p_code->finish();
9686722001-07-25Martin Stjernholm  if (stringp (in_content) && ctx->make_p_code) { in_content = p_code; ctx->state_updated++; }
cf042f2001-07-21Martin Stjernholm  subevaler->p_code = 0;
02a6562001-07-09Martin Stjernholm  ctx->make_p_code = orig_make_p_code; // Reset before do_return. }
a08cd72001-06-09Martin Stjernholm  subevaler = 0; }
b483382000-06-30Martin Stjernholm 
d52f8f2001-05-19Martin Stjernholm  if (do_process) { EXEC_CALLBACK (ctx, evaler, exec, do_process, id); EXEC_ARRAY (ctx, evaler, exec, do_process);
49897a2000-02-13Martin Stjernholm  }
b483382000-06-30Martin Stjernholm  } } while (eval_state != EVSTAT_LAST_ITER);
d52f8f2001-05-19Martin Stjernholm  eval_state = EVSTAT_ITER_DONE;
6dfc812001-07-20Martin Stjernholm  // Fall through.
4c2b512001-05-17Martin Stjernholm 
b483382000-06-30Martin Stjernholm  case EVSTAT_ITER_DONE:
02a6562001-07-09Martin Stjernholm  if (array|function(RequestID,void|PCode:array) do_return = [array|function(RequestID,void|PCode:array)] this_object()->do_return) { EXEC_CALLBACK (ctx, evaler, exec, do_return, id, objectp (in_content) && in_content);
b483382000-06-30Martin Stjernholm  if (exec) {
02a6562001-07-09Martin Stjernholm  // We don't use EXEC_ARRAY here since there's no idea // to come back even if any streaming should be done.
a08cd72001-06-09Martin Stjernholm  _exec_array (ctx, evaler, exec, "do_return"); // Might unwind.
e86f2d2000-02-15Martin Stjernholm  exec = 0;
49897a2000-02-13Martin Stjernholm  }
b483382000-06-30Martin Stjernholm  }
ac69772000-09-08Martin Stjernholm  else if (result == nil && !(flags & FLAG_EMPTY_ELEMENT)) {
7dd3f82001-04-18Martin Stjernholm  if (result_type->parser_prog == PNone) {
fe628d2001-03-01Martin Stjernholm  if (content_type->name != result_type->name) {
4c2b512001-05-17Martin Stjernholm  THIS_TAG_DEBUG ("Assigning content to result after "
d52f8f2001-05-19Martin Stjernholm  "conversion from %s to %s\n",
4c2b512001-05-17Martin Stjernholm  content_type->name, result_type->name);
ac69772000-09-08Martin Stjernholm  result = result_type->encode (content, content_type); } else { THIS_TAG_DEBUG ("Assigning content to result\n");
b483382000-06-30Martin Stjernholm  result = content;
ac69772000-09-08Martin Stjernholm  }
b483382000-06-30Martin Stjernholm  } else
ac69772000-09-08Martin Stjernholm  if (stringp (content)) { if (!exec) { THIS_TAG_DEBUG ("Parsing content with exec array " "for assignment to result\n"); exec = ({content}); }
a08cd72001-06-09Martin Stjernholm  _exec_array (ctx, evaler, exec, "content parse"); // Might unwind.
b483382000-06-30Martin Stjernholm  exec = 0; }
ac69772000-09-08Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  }
d52f8f2001-05-19Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  mixed conv_result = nil; // Result converted to the expected type. if (result != nil) CONV_RESULT (result, result_type, conv_result, type);
ac69772000-09-08Martin Stjernholm #ifdef DEBUG
02a6562001-07-09Martin Stjernholm  else THIS_TAG_DEBUG ("Skipping nil result\n");
ac69772000-09-08Martin Stjernholm #endif
4c2b512001-05-17Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  THIS_TAG_DEBUG_LEAVE_SCOPE (ctx, this_object()); LEAVE_SCOPE (ctx, this_object());
f997202000-01-18Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  if (ctx->new_runtime_tags) _handle_runtime_tags (ctx, evaler);
ffa6932000-02-20Martin Stjernholm 
f853b82001-07-19Martin Stjernholm #define CLEANUP do { \
02a6562001-07-09Martin Stjernholm  if (in_args) args = in_args; \ if (in_content) content = in_content; \ ctx->make_p_code = orig_make_p_code; \ if (orig_tag_set) ctx->tag_set = orig_tag_set; \
cf042f2001-07-21Martin Stjernholm  if (flags & FLAG_DONT_CACHE_RESULT && up) \
6dfc812001-07-20Martin Stjernholm  up->flags |= FLAG_DONT_CACHE_RESULT; \
02a6562001-07-09Martin Stjernholm  ctx->frame = up; \
f853b82001-07-19Martin Stjernholm  FRAME_DEPTH_MSG ("%*s%O frame_depth decrease line %d\n", \ ctx->frame_depth, "", this_object(), \ __LINE__); \
02a6562001-07-09Martin Stjernholm  ctx->frame_depth--; \ } while (0)
f853b82001-07-19Martin Stjernholm  CLEANUP;
02a6562001-07-09Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Done%s\n", flags & FLAG_DONT_CACHE_RESULT ? " (don't cache result)" : "");
02a6562001-07-09Martin Stjernholm  TRACE_LEAVE (""); return conv_result; }) { // Exception handling. THIS_TAG_DEBUG_LEAVE_SCOPE (ctx, this_object()); LEAVE_SCOPE (ctx, this_object()); unwind: if (objectp (err) && ([object] err)->thrown_at_unwind) #ifdef MODULE_DEBUG if (eval_state == EVSTAT_NONE) fatal_error ("Can't save parser state when evaluating arguments.\n"); else #endif { string action;
d52f8f2001-05-19Martin Stjernholm  UNWIND_STATE ustate = ctx->unwind_state; if (!ustate) ustate = ctx->unwind_state = ([]);
ed81751999-12-11Martin Stjernholm #ifdef DEBUG
02a6562001-07-09Martin Stjernholm  if (ustate[this_object()]) fatal_error ("Frame already has an unwind state.\n");
ed81751999-12-11Martin Stjernholm #endif
d52f8f2001-05-19Martin Stjernholm  if (ustate->exec_left) { exec = [array] ustate->exec_left; m_delete (ustate, "exec_left");
b483382000-06-30Martin Stjernholm  }
d52f8f2001-05-19Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  if (err == this_object() || exec && sizeof (exec) && err == exec[0])
d52f8f2001-05-19Martin Stjernholm  // This frame or a frame in the exec array wants to stream. if (evaler->read && evaler->unwind_safe) { // Rethrow to continue in parent since we've already done // the appropriate do_process stuff in this frame in // either case. mixed piece = evaler->read();
02a6562001-07-09Martin Stjernholm  if (err = catch {
d52f8f2001-05-19Martin Stjernholm  if (type->sequential) SET_SEQUENTIAL (ustate->stream_piece, piece, "stream piece"); else SET_NONSEQUENTIAL (ustate->stream_piece, piece, type, "stream piece"); }) break unwind;
02a6562001-07-09Martin Stjernholm  if (err == this_object()) err = 0;
d52f8f2001-05-19Martin Stjernholm  if (orig_tag_set) ctx->tag_set = orig_tag_set, orig_tag_set = 0; action = "break"; THIS_TAG_TOP_DEBUG ("Breaking to parent frame to do streaming\n"); } else { // Can't stream since the parser doesn't allow that. // Just continue. m_delete (ustate, "stream_piece"); action = "continue"; THIS_TAG_TOP_DEBUG ("Not streaming since the parser " "doesn't allow that\n"); } else if (!zero_type (ustate->stream_piece)) { // Got a stream piece from a subframe. We handle it above; // store the state and tail recurse. piece = ustate->stream_piece;
b483382000-06-30Martin Stjernholm  m_delete (ustate, "stream_piece"); action = "continue";
e9ff832001-06-27Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Continuing with stream piece.\n");
b483382000-06-30Martin Stjernholm  }
d52f8f2001-05-19Martin Stjernholm  else { action = "break"; // Some other reason - back up to the top. THIS_TAG_TOP_DEBUG ("Interrupted\n"); }
ed81751999-12-11Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  ustate[this_object()] = ({err, eval_state, in_args, in_content, iter, subevaler, piece, exec, orig_tag_set, ctx->new_runtime_tags, orig_make_p_code,
ac69772000-09-08Martin Stjernholm #ifdef DEBUG
02a6562001-07-09Martin Stjernholm  debug_iter,
ac69772000-09-08Martin Stjernholm #endif
02a6562001-07-09Martin Stjernholm  });
d52f8f2001-05-19Martin Stjernholm  TRACE_LEAVE (action);
02a6562001-07-09Martin Stjernholm  switch (action) { case "break": // Throw and handle in parent frame.
56532d1999-12-19Martin Stjernholm #ifdef MODULE_DEBUG
02a6562001-07-09Martin Stjernholm  if (!evaler->unwind_state) fatal_error ("Trying to unwind inside an evaluator " "that isn't unwind safe.\n");
56532d1999-12-19Martin Stjernholm #endif
02a6562001-07-09Martin Stjernholm  throw (this_object()); case "continue": // Continue in this frame with the stored state. continue process_tag; } fatal_error ("Should not get here.\n"); }
7dd3f82001-04-18Martin Stjernholm 
6dfc812001-07-20Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Exception%s\n", flags & FLAG_DONT_CACHE_RESULT ? " (don't cache result)" : "");
02a6562001-07-09Martin Stjernholm  TRACE_LEAVE ("exception");
f853b82001-07-19Martin Stjernholm  err = catch { ctx->handle_exception (err, evaler); // Will rethrow unknown errors. }; CLEANUP; if (err) throw (err);
02a6562001-07-09Martin Stjernholm  return result = nil;
ac69772000-09-08Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  fatal_error ("Should not get here.\n"); } fatal_error ("Should not get here.\n");
ed81751999-12-11Martin Stjernholm  }
02a6562001-07-09Martin Stjernholm  array _save() {
9686722001-07-25Martin Stjernholm  THIS_TAG_TOP_DEBUG ("Saving persistent state\n");
02a6562001-07-09Martin Stjernholm  return ({args, content, flags, content_type, result_type, this_object()->raw_tag_text, this_object()->save && this_object()->save()}); }
26ff092000-01-21Martin Stjernholm 
02a6562001-07-09Martin Stjernholm  void _restore (array saved)
ed81751999-12-11Martin Stjernholm  {
02a6562001-07-09Martin Stjernholm  [args, content, flags, content_type, result_type, string raw_tag_text, mixed user_saved] = saved; if (raw_tag_text) this_object()->raw_tag_text = raw_tag_text; if (user_saved) restore (user_saved);
ed81751999-12-11Martin Stjernholm  }
fc75492001-06-22Marcus Comstedt 
02a6562001-07-09Martin Stjernholm  Frame _clone_empty()
fc75492001-06-22Marcus Comstedt  {
02a6562001-07-09Martin Stjernholm  Frame new = object_program (this_object())(); new->flags = flags; new->tag = tag; return new;
fc75492001-06-22Marcus Comstedt  }
112b012001-08-10Martin Nilsson  //! @ignore
02a6562001-07-09Martin Stjernholm  MARK_OBJECT;
112b012001-08-10Martin Nilsson  //! @endignore
02a6562001-07-09Martin Stjernholm  string _sprintf()
fc75492001-06-22Marcus Comstedt  {
02a6562001-07-09Martin Stjernholm  return "RXML.Frame(" + (tag && [string] tag->name) + ")" + OBJ_COUNT;
fc75492001-06-22Marcus Comstedt  }
ed81751999-12-11Martin Stjernholm }
5c7a6b2001-06-25Martin Stjernholm // Global services.
49897a2000-02-13Martin Stjernholm 
f134b92000-11-27Martin Stjernholm //! Shortcuts to some common functions in the current context (see the
d4768a2001-05-18Martin Stjernholm //! corresponding functions in the @[Context] class for details).
74fa382001-07-25Martin Stjernholm final mixed get_var (string|array(string|int) var, void|string scope_name, void|Type want_type)
be73272001-06-18Martin Stjernholm  {return RXML_CONTEXT->get_var (var, scope_name, want_type);}
f134b92000-11-27Martin Stjernholm final mixed user_get_var (string var, void|string scope_name, void|Type want_type)
be73272001-06-18Martin Stjernholm  {return RXML_CONTEXT->user_get_var (var, scope_name, want_type);}
74fa382001-07-25Martin Stjernholm final mixed set_var (string|array(string|int) var, mixed val, void|string scope_name)
be73272001-06-18Martin Stjernholm  {return RXML_CONTEXT->set_var (var, val, scope_name);}
f134b92000-11-27Martin Stjernholm final mixed user_set_var (string var, mixed val, void|string scope_name)
be73272001-06-18Martin Stjernholm  {return RXML_CONTEXT->user_set_var (var, val, scope_name);}
74fa382001-07-25Martin Stjernholm final void delete_var (string|array(string|int) var, void|string scope_name)
be73272001-06-18Martin Stjernholm  {RXML_CONTEXT->delete_var (var, scope_name);}
f134b92000-11-27Martin Stjernholm final void user_delete_var (string var, void|string scope_name)
be73272001-06-18Martin Stjernholm  {RXML_CONTEXT->user_delete_var (var, scope_name);}
5ad5292000-03-09Martin Stjernholm 
f134b92000-11-27Martin Stjernholm final void run_error (string msg, mixed... args)
8cb24d2000-02-13Martin Stjernholm //! Throws an RXML run error with a dump of the parser stack in the //! current context. This is intended to be used by tags for errors //! that can occur during normal operation, such as when the //! connection to an SQL server fails.
49897a2000-02-13Martin Stjernholm {
8cb24d2000-02-13Martin Stjernholm  if (sizeof (args)) msg = sprintf (msg, @args);
51f88b2000-06-23Martin Stjernholm  array bt = backtrace();
be73272001-06-18Martin Stjernholm  throw (Backtrace ("run", msg, RXML_CONTEXT, bt[..sizeof (bt) - 2]));
49897a2000-02-13Martin Stjernholm }
f134b92000-11-27Martin Stjernholm final void parse_error (string msg, mixed... args)
8cb24d2000-02-13Martin Stjernholm //! Throws an RXML parse error with a dump of the parser stack in the //! current context. This is intended to be used for programming //! errors in the RXML code, such as lookups in nonexisting scopes and //! invalid arguments to a tag.
49897a2000-02-13Martin Stjernholm {
8cb24d2000-02-13Martin Stjernholm  if (sizeof (args)) msg = sprintf (msg, @args);
51f88b2000-06-23Martin Stjernholm  array bt = backtrace();
be73272001-06-18Martin Stjernholm  throw (Backtrace ("parse", msg, RXML_CONTEXT, bt[..sizeof (bt) - 2]));
49897a2000-02-13Martin Stjernholm }
f134b92000-11-27Martin Stjernholm final void fatal_error (string msg, mixed... args)
8cb24d2000-02-13Martin Stjernholm //! Throws a Pike error that isn't catched and handled anywhere. It's
d4768a2001-05-18Martin Stjernholm //! just like the common @[error] function, but includes the RXML //! frame backtrace.
49897a2000-02-13Martin Stjernholm {
8cb24d2000-02-13Martin Stjernholm  if (sizeof (args)) msg = sprintf (msg, @args); array bt = backtrace(); throw_fatal (({msg, bt[..sizeof (bt) - 2]})); }
f134b92000-11-27Martin Stjernholm final void throw_fatal (mixed err)
8cb24d2000-02-13Martin Stjernholm //! Mainly used internally to throw an error that includes the RXML //! frame backtrace. { if (arrayp (err) && sizeof (err) == 2 || objectp (err) && !err->is_RXML_Backtrace && err->is_generic_error) { string msg; if (catch (msg = err[0])) throw (err); if (stringp (msg) && !has_value (msg, "\nRXML frame backtrace:\n")) {
c64acf2000-02-17Martin Stjernholm  string descr = Backtrace (0, 0)->describe_rxml_backtrace (1);
5ed0132000-02-13Martin Stjernholm  if (sizeof (descr)) { if (sizeof (msg) && msg[-1] != '\n') msg += "\n"; msg += "RXML frame backtrace:\n" + descr; catch (err[0] = msg); }
8cb24d2000-02-13Martin Stjernholm  } } throw (err);
49897a2000-02-13Martin Stjernholm }
1cfbeb2000-11-06Martin Stjernholm final mixed rxml_index (mixed val, string|int|array(string|int) index, string scope_name, Context ctx, void|Type want_type) //! Index the value according to RXML type rules and returns the //! result. Throws RXML exceptions on any errors. If index is an //! array, its elements are used to successively subindex the value,
4c2b512001-05-17Martin Stjernholm //! e.g. @tt{({"a", 2, "b"})@} corresponds to @tt{val["a"][2]["c"]@}. //! @[scope_name] is used to identify the context for the indexing.
1cfbeb2000-11-06Martin Stjernholm //! //! The special RXML index rules are: //!
112b012001-08-10Martin Nilsson //! @list
151fa92001-04-19Johan Sundström //! @item //! Arrays are indexed with 1 for the first element, or //! alternatively -1 for the last. Indexing an array of size n with //! 0, n+1 or greater, -n-1 or less, or with a non-integer is an //! error. //! @item //! Strings, along with integers and floats, are treated as simple //! scalar types which aren't really indexable. If a scalar type is //! indexed with 1 or -1, it produces itself instead of generating //! an error. (This is a convenience to avoid many special cases //! when treating both arrays and scalar types.) //! @item
d4768a2001-05-18Martin Stjernholm //! If a value is an object which has an @tt{rxml_var_eval@} //! identifier, it's treated as an @[RXML.Value] object and the //! @[RXML.Value.rxml_var_eval] function is called to produce its //! value.
151fa92001-04-19Johan Sundström //! @item //! If a value which is about to be indexed is an object which has a
d4768a2001-05-18Martin Stjernholm //! @tt{`[]@} function, it's called as an @[RXML.Scope] object (see //! @ref{RXML.Scope.`[]@}).
151fa92001-04-19Johan Sundström //! @item //! Both the special value nil and the undefined value (a zero with //! zero_type 1) may be used to signify no value at all, and both //! will be returned as the undefined value. //! @endlist
1cfbeb2000-11-06Martin Stjernholm //!
d4768a2001-05-18Martin Stjernholm //! If the @[want_type] argument is set, the result value is converted //! to that type with @[RXML.Type.encode]. If the value can't be //! converted, an RXML error is thrown.
1cfbeb2000-11-06Martin Stjernholm //! //! This function is mainly for internal use; you commonly want to use
d4768a2001-05-18Martin Stjernholm //! @[get_var], @[set_var], @[user_get_var] or @[user_set_var] //! instead.
1cfbeb2000-11-06Martin Stjernholm { #ifdef MODULE_DEBUG if (arrayp (index) ? !sizeof (index) : !(stringp (index) || intp (index)))
d52f8f2001-05-19Martin Stjernholm  fatal_error ("Invalid index specifier.\n");
1cfbeb2000-11-06Martin Stjernholm #endif
2b5f472001-02-11Martin Stjernholm  int scope_got_type = 0;
1cfbeb2000-11-06Martin Stjernholm  array(string|int) idxpath;
2b5f472001-02-11Martin Stjernholm  if (arrayp (index)) idxpath = index, index = index[0]; else idxpath = ({0});
1cfbeb2000-11-06Martin Stjernholm 
2b5f472001-02-11Martin Stjernholm  for (int i = 1;; i++) {
1cfbeb2000-11-06Martin Stjernholm  // stringp was not really a good idea. if( arrayp( val ) /*|| stringp( val )*/ ) if (intp (index) && index) if( (index > sizeof( val )) || ((index < 0) && (-index > sizeof( val ) )) ) parse_error( "Index %d out of range for array of size %d in %s.\n", index, sizeof (val), scope_name ); else if( index < 0 ) val = val[index]; else val = val[index-1]; else
a835e82001-07-25Martin Stjernholm  parse_error( "Cannot index the array in %s with %s.\n", scope_name, format_short (index) );
1cfbeb2000-11-06Martin Stjernholm  else if (val == nil)
a835e82001-07-25Martin Stjernholm  parse_error ("%s produced no value to index with %s.\n", scope_name, format_short (index));
1cfbeb2000-11-06Martin Stjernholm  else if( objectp( val ) && val->`[] ) {
fe628d2001-03-01Martin Stjernholm #ifdef MODULE_DEBUG Scope scope = [object(Scope)] val; #endif
1cfbeb2000-11-06Martin Stjernholm  if (zero_type (
2b5f472001-02-11Martin Stjernholm  val = ([object(Scope)] val)->`[]( index, ctx, scope_name, i == sizeof (idxpath) && (scope_got_type = 1, want_type))))
1cfbeb2000-11-06Martin Stjernholm  val = nil;
fe628d2001-03-01Martin Stjernholm #ifdef MODULE_DEBUG
2531fa2001-03-23Martin Stjernholm  else if (mixed err = scope_got_type && want_type && val != nil &&
0c9e5b2001-03-13Martin Stjernholm  !(objectp (val) &&