2001-07-20
2001-07-20 06:34:31 by Martin Stjernholm <mast@lysator.liu.se>
-
6dfc81473a1b3d92fe61e767a0d0e83fa2f5fca8
(744 lines)
(+483/-261)
[
Show
| Annotate
]
Branch: 5.2
Implemented p-coding of the (mostly) evaluated content: Three new
flags FLAG_GET_RAW_CONTENT, FLAG_GET_EVALED_CONTENT,
FLAG_DONT_CACHE_RESULT and the variable Frame.evaled_content control
it. Changed the handling of stale p-code; users should now check with
PCode.is_stale instead of watching for a special return value. Allow
changes of the error message in Backtrace through the hideous standard
exception object "API". Fixed some duplicate calls to handle_exception
on the same level. Also some docstring improvements and various small
fixes.
Rev: server/etc/modules/RXML.pmod/module.pmod:1.209
2:
//
// Created 1999-07-30 by Martin Stjernholm.
//
- // $Id: module.pmod,v 1.208 2001/07/20 00:36:18 mast Exp $
+ // $Id: module.pmod,v 1.209 2001/07/20 06:34:31 mast Exp $
// Kludge: Must use "RXML.refs" somewhere for the whole module to be
// loaded correctly.
401:
if (mixed err = catch { \
_res = _frame->_eval (_ctx, _parser, _type); \
if (PCode p_code = _parser->p_code) \
- p_code->add_frame (_frame, _ctx); \
- }) { \
- if (PCode p_code = _parser->p_code) \
- p_code->add_frame (_frame, _ctx); \
+ p_code->add_frame (_ctx, _frame, _res); \
+ }) \
if (objectp (err) && ([object] err)->thrown_at_unwind) { \
UNWIND_STATE ustate = _ctx->unwind_state; \
if (!ustate) ustate = _ctx->unwind_state = ([]); \
418:
throw (_parser); \
} \
else { \
- /* Will rethrow unknown errors. */ \
- _ctx->handle_exception (err, _parser); \
- _res = nil; \
+ if (PCode p_code = _parser->p_code) \
+ p_code->add_frame (_ctx, _frame, PCode); \
+ throw (err); \
} \
- } \
+
} while (0)
final mixed handle_tag (TagSetParser parser, mapping(string:string) args,
1296:
//! Current evaluation recursion depth. This might be more than the
//! number of frames in the @[frame] linked chain.
- int max_frame_depth = 100;
+ constant max_frame_depth = 100;
//! Maximum allowed evaluation recursion depth.
RequestID id;
1341:
//! default scope is chosen as appropriate if var cannot be split,
//! unless @[scope_name] is a nonzero integer in which case it's
//! returned in the scope position in the array (useful to detect
- //! whether @[var] actually was splitted or not).
+ //! whether @[var] actually was split or not).
//!
//! @tt{".."@} in the var string quotes a literal @tt{"."@}, e.g.
//! @tt{"yow...cons..yet"@} is separated into @tt{"yow."@} and
1438:
if (arrayp (var))
if (sizeof (var) > 1) {
index = var[-1];
- var = var[..sizeof (var) - 1];
- vars = rxml_index (vars, var, scope_name, this_object());
- scope_name += "." + (array(string)) var * ".";
+ 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, val)] = val;
}
- else index = var[0];
- else index = var;
+ 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;
+ }
if (objectp (vars) && vars->`[]=)
return ([object(Scope)] vars)->`[]= (index, val, this_object(), scope_name);
1569:
//! Adds or replaces the specified scope at the global level. A
//! scope can be a mapping or an @[RXML.Scope] object. A global
//! @tt{"_"@} scope may also be defined this way.
+ //!
+ //! @note
+ //! Does not track variable changes for caching purposes like
+ //! @[set_var] does.
{
if (scopes[scope_name])
if (scope_name == "_") {
1591:
int extend_scope (string scope_name, SCOPE_TYPE vars)
//! Adds or extends the specified scope at the global level.
//! Returns 1 on success and 0 on failure.
+ //!
+ //! @note
+ //! Does not track variable changes for caching purposes like
+ //! @[set_var] does.
{
if (scopes[scope_name]) {
SCOPE_TYPE oldvars;
1623:
void remove_scope (string scope_name)
//! Removes the named scope from the global level, if it exists.
+ //!
+ //! @note
+ //! Does not track variable changes for caching purposes like
+ //! @[set_var] does.
{
#ifdef MODULE_DEBUG
if (scope_name == "_") fatal_error ("Cannot remove current scope.\n");
1658: Inside #if defined(MODULE_DEBUG)
{
#ifdef MODULE_DEBUG
if (tag->plugin_name)
- fatal_error ("Can't currently handle adding of plugin tags at runtime.\n");
+ fatal_error ("Cannot handle plugin tags added at runtime.\n");
#endif
if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags();
new_runtime_tags->add_tag (tag);
1710:
else
if (err->target) {
ctx->frame = err->cur_frame;
- err = catch (parse_error ("There is no surround frame %s.\n",
+ err = catch (parse_error ("There is no surrounding frame %s.\n",
stringp (err->target) ?
sprintf ("with scope %O", err->target) :
sprintf ("%O", err->target)));
1738:
else
msg = err->msg;
if (evaluator->report_error (msg)) {
- if (compile_error && evaluator->p_code)
- evaluator->p_code->add (CompiledError (err));
+ if (compile_error && evaluator->p_code) {
+ CompiledError comp_err = CompiledError (err);
+ evaluator->p_code->add (RXML_CONTEXT, comp_err, comp_err);
+ }
return;
}
}
1750:
throw_fatal (err);
}
- final array(mixed|PCode) eval_and_compile (Type type, string to_parse)
+ final array(mixed|PCode) eval_and_compile (Type type, string to_parse,
+ void|int stale_safe)
//! 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
- //! and the generated @[RXML.PCode] object in the second.
+ //! 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.
{
mixed res;
PCode p_code;
int orig_make_p_code = make_p_code;
- init_make_p_code();
- Parser parser = type->get_parser (this_object(), tag_set, 0, 1);
+ make_p_code = 1;
+ Parser parser = type->get_parser (
+ this_object(), tag_set, 0, stale_safe ? RenewablePCode (0) : PCode (0));
mixed err = catch {
parser->write_end (to_parse);
res = parser->eval();
1783: Inside #if defined(MODULE_DEBUG)
#ifdef MODULE_DEBUG
if (in_use || frame) fatal_error ("Context already in use.\n");
#endif
- if ((make_p_code = _make_p_code) > 0) init_make_p_code();
- return top_level_type->get_parser (this_object(), tag_set, 0, _make_p_code);
+ return top_level_type->get_parser (this_object(), tag_set, 0,
+ make_p_code = _make_p_code);
}
#ifdef DEBUG
1868:
// The PikeCompile object for collecting any produce Pike byte code
// during p-code compilation.
- final void init_make_p_code()
- {
- make_p_code = 1;
- if (!p_code_comp) p_code_comp = PikeCompile();
- }
-
+
static void create (void|TagSet _tag_set, void|RequestID _id)
// Normally TagSet.`() should be used instead of this.
{
2063:
}
}
+ 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);
+ }
+
string _sprintf() {return "RXML.Backtrace(" + (type || "") + ")";}
}
2165:
// Flags tested in the Frame object:
- // constant FLAG_PARENT_SCOPE = 0x00000100;
- //
- // This feature proved unnecessary and no longer exists.
- //
- // If set, exec arrays will be interpreted in the scope of the parent
- // tag, rather than in the current one.
-
- constant FLAG_NO_IMPLICIT_ARGS = 0x00000200;
- // If set, the parser won't apply any implicit arguments. FIXME: Not
- // implemented.
-
+
constant FLAG_STREAM_RESULT = 0x00000400;
//! If set, the @[do_process] function will be called repeatedly until
//! it returns 0 or no more content is wanted.
2237:
//! destructively changed to contain the p-code. This affects strings
//! if the result type has a parser.
+ 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.
+
class Frame
//! A tag instance. A new frame is normally created for every parsed
//! tag in the source document. It might be reused both when the
2272:
//! by p-code.
mapping(string:mixed)|EVAL_ARGS_FUNC args;
- //! 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.
+ //! 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.
Type content_type;
- //! The type of the content.
+ //! 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.
mixed content;
//! The content, if any. Set before @[do_process] and @[do_return]
2298:
//! 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.
+ //!
+ //! @note
+ //! This variable is assumed to be static; see the note for
+ //! @[content_type] for further details.
- mixed result = nil;
+ mixed result;
//! The result, which is assumed to be either @[RXML.nil] or a valid
//! value according to result_type. The exec arrays returned by e.g.
//! @[do_return] changes this. It may also be set directly.
2314:
//!
//! Set this to introduce a new variable scope that will be active
//! during parsing of the content and return values.
+ //!
+ //! @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.
//! @decl optional string scope_name;
//!
2340:
//! parser. The tags are not inherited by subparsers.
//!
//! @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.
+ //! This variable is assumed to be static; see the note for
+ //! @[additional_tags] for further details.
//! @decl optional Frame parent_frame;
//!
2503:
//! had at the call to @[save].
//!
//! @note
- //! A frame might be reused without a prior call to this function,
- //! if the same frame object has been cached since the previous
- //! occasion.
+ //! A frame might be reevaluated without a prior call to this
+ //! function, if it's the same frame object since the call to
+ //! @[save].
//!
//! @note
//! This function is used to decode dumped p-code that's read from
2516:
//! It must never restore an invalid state which might cause errors
//! or invalid results in later calls to the @tt{do_*@} functions.
- //! @decl PCode result_p_code;
+ //! @decl PCode evaled_content;
//!
- //! Set this to an @[RXML.PCode] object to make this tag a p-code
- //! cache frame. When the content is evaluated, the p-code object
- //! will receive the result of the evaluation, with the exception of
- //! any nested p-code cache frames which remains unevaluated.
+ //! 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]).
// Services:
2678:
else {
CHECK_RAW_TEXT;
- // Format a new tag, as like the original as possible.
+ // Format a new tag, as close to the original as possible.
if (flags & FLAG_PROC_INSTR) {
if (content) {
2812:
{
PCode p_code = 0;
if ((ctx->make_p_code = flags & FLAG_COMPILE_RESULT)) {
- ctx->init_make_p_code();
+
p_code = RenewablePCode (0);
p_code->source = [string] elem;
}
3023:
// in args. Might be destructive on raw_args.
{
mixed err = catch {
- if (ctx->frame_depth >= ctx->max_frame_depth)
+ if (ctx->frame_depth >= Context.max_frame_depth)
_run_error ("Too deep recursion -- exceeding %d nested tags.\n",
- ctx->max_frame_depth);
+ Context.max_frame_depth);
EVAL_ARGS_FUNC|string func;
3248:
int debug_iter = 1;
#endif
object(Parser)|object(PCode) subevaler;
- mixed piece = nil;
+ mixed piece;
array exec = 0;
TagSet orig_tag_set; // Flags that we added additional_tags to ctx->tag_set.
//ctx->new_runtime_tags
3323: Inside #if defined(DEBUG)
if (content && !stringp (content))
fatal_error ("content is not a string in unparsed frame: %O.\n", content);
#endif
- if (flags & FLAG_COMPILE_INPUT || ctx->make_p_code < 0)
- ctx->init_make_p_code();
+
THIS_TAG_TOP_DEBUG ("Evaluating%s unparsed\n",
ctx->make_p_code ? " and compiling" : "");
if (PCode p_code = ctx->make_p_code &&
- (evaler->is_RXML_PCode ? evaler : evaler->p_code))
+ (evaler->is_RXML_PCode ? evaler : evaler->p_code)) {
// FIXME: Perhaps make a new PCode object if the evaler
// doesn't provide one?
-
+ if (!ctx->p_code_comp) ctx->p_code_comp = PikeCompile();
in_args = _prepare (ctx, type, args && args + ([]), p_code);
-
+ }
else
_prepare (ctx, type, args && args + ([]), 0);
in_content = content;
- content = nil;
+
}
else {
THIS_TAG_TOP_DEBUG ("Evaluating with constant arguments and content\n");
_prepare (ctx, type, 0, 0);
}
-
+ result = piece = nil;
eval_state = EVSTAT_BEGIN;
}
3402:
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this_object());
ENTER_SCOPE (ctx, this_object());
}
+ if (flags & FLAG_UNPARSED) content = nil;
eval_state = EVSTAT_ENTERED;
- /* Fall through. */
+ // Fall through.
case EVSTAT_ENTERED:
case EVSTAT_LAST_ITER:
3442:
finished++;
if (subevaler)
finished = 0; // Continuing an unwound subevaler.
- else if (!in_content || in_content == "")
+ else if (!in_content || in_content == "") {
+ if (flags & FLAG_GET_EVALED_CONTENT) {
+ this_object()->evaled_content = PCode (content_type);
+ this_object()->evaled_content->finish();
+ }
break eval_content; // No content to handle.
-
+ }
else if (stringp (in_content)) {
if (flags & FLAG_EMPTY_ELEMENT)
parse_error ("This tag doesn't handle content.\n");
3451:
if (finished > 1)
// Looped once. Always compile since it's
// likely we'll loop again.
- ctx->init_make_p_code();
+ 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, ctx->make_p_code);
+ evaler, p_code);
subevaler->_local_tag_set = 1;
THIS_TAG_DEBUG ("Iter[%d]: Parsing%s content %s "
"with %O from local_tags\n", debug_iter,
3464:
}
else {
subevaler = content_type->get_parser (
- ctx, ctx->tag_set, evaler, ctx->make_p_code);
+ ctx, ctx->tag_set, evaler, p_code);
#ifdef DEBUG
if (content_type->parser_prog != PNone)
THIS_TAG_DEBUG ("Iter[%d]: Parsing%s content %s "
3475:
}
if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER)) {
subevaler->recover_errors = 1;
- if (PCode p_code = subevaler->p_code) p_code->recover_errors = 1;
+ if (p_code) {
+ p_code->recover_errors = 1;
+ if ((p_code = p_code->p_code))
+ p_code->recover_errors = 1;
}
-
+ }
+ }
subevaler->finish (in_content); // Might unwind.
finished = 1;
}
3485:
THIS_TAG_DEBUG ("Iter[%d]: Evaluating compiled content\n",
debug_iter);
subevaler = in_content;
+ if (flags & FLAG_GET_EVALED_CONTENT)
+ subevaler->p_code = this_object()->evaled_content =
+ PCode (content_type, subevaler->tag_set, ctx);
}
eval_sub:
3537:
}
}
- if (subevaler->p_code) ctx->init_make_p_code();
+
subevaler->finish(); // Might unwind.
finished = 1;
} while (1); // Only loops when an unwound subevaler has been recovered.
3557:
}
} while (eval_state != EVSTAT_LAST_ITER);
eval_state = EVSTAT_ITER_DONE;
- /* Fall through. */
+ // Fall through.
case EVSTAT_ITER_DONE:
if (array|function(RequestID,void|PCode:array) do_return =
3616:
if (in_content) content = in_content; \
ctx->make_p_code = orig_make_p_code; \
if (orig_tag_set) ctx->tag_set = orig_tag_set; \
+ if (flags & FLAG_DONT_CACHE_RESULT) \
+ up->flags |= FLAG_DONT_CACHE_RESULT; \
ctx->frame = up; \
FRAME_DEPTH_MSG ("%*s%O frame_depth decrease line %d\n", \
ctx->frame_depth, "", this_object(), \
3625:
CLEANUP;
- THIS_TAG_TOP_DEBUG ("Done\n");
+ THIS_TAG_TOP_DEBUG ("Done%s\n",
+ flags & FLAG_DONT_CACHE_RESULT ?
+ " (don't cache result)" : "");
TRACE_LEAVE ("");
return conv_result;
3716:
fatal_error ("Should not get here.\n");
}
- THIS_TAG_TOP_DEBUG ("Exception\n");
+ THIS_TAG_TOP_DEBUG ("Exception%s\n",
+ flags & FLAG_DONT_CACHE_RESULT ?
+ " (don't cache result)" : "");
TRACE_LEAVE ("exception");
err = catch {
ctx->handle_exception (err, evaler); // Will rethrow unknown errors.
4241:
if ((err = catch {
context->handle_exception (err, this_object()); // May throw.
})) {
+ FRAME_DEPTH_MSG ("%*s%O frame_depth increase line %d\n",
+ context->frame_depth, "", varref, __LINE__);
context->frame_depth--;
throw (err);
}
4248:
}
if (PCode p_code = evaler->p_code)
- p_code->add (VarRef (splitted[0], splitted[1..], encoding, want_type));
+ p_code->add (context,
+ VarRef (splitted[0], splitted[1..], encoding, want_type), val);
}
FRAME_DEPTH_MSG ("%*s%O frame_depth increase line %d\n",
context->frame_depth, "", varref, __LINE__);
4516:
if (in) add (in);
if (p_code) {
string data = get();
- p_code->add (data);
+ p_code->add (context, data, data);
add (data);
}
}
4686:
//! directly, otherwise a new object is created.
{
PCode p_code = 0;
- if (make_p_code)
+ if (make_p_code) {
if (objectp (make_p_code)) (p_code = make_p_code)->reset (this_object(), tag_set);
else p_code = PCode (this_object(), tag_set);
- #ifdef DEBUG
- if (make_p_code && !ctx->p_code_comp)
- error ("Context has no p_code_comp for p-coding parser.\n");
- #endif
+ if (!ctx->p_code_comp) ctx->p_code_comp = PikeCompile();
+ }
Parser p;
if (_p_cache) { // It's a tag set parser.
5814:
void delete (Context ctx) {ctx->delete_var (var, scope);}
- string name() {return scope + "." + (arrayp (var) ? (array(string)) var * "." : var);}
+ string name()
+ {
+ return scope && var &&
+ map (arrayp (var) ? ({scope}) + (array(string)) var : ({scope, (string) var}),
+ replace, ".", "..") * ".";
+ }
- MARK_OBJECT;
- string _sprintf() {return "RXML.VarRef(" + name() + ")" + OBJ_COUNT;}
-
- mixed _encode()
+ array _encode()
{
return ({ scope, var, encoding, want_type });
}
- void _decode(mixed v)
+ void _decode(array v)
{
[scope, var, encoding, want_type] = v;
}
-
+ MARK_OBJECT;
+ string _sprintf() {return "RXML.VarRef(" + name() + ")" + OBJ_COUNT;}
}
-
+ class ScopeChange (static mapping(string:mixed) settings)
+ // A compiled-in change of some scope variables. Used when caching
+ // results.
+ {
+ constant is_RXML_ScopeChange = 1;
+ constant is_RXML_encodable = 1;
+ constant is_RXML_p_code_entry = 1;
+
+ mixed get (Context ctx)
+ {
+ foreach (indices (settings), string encoded_var) {
+ array var = decode_value (encoded_var);
+ #ifdef DEBUG
+ if (TAG_DEBUG_TEST (ctx->frame))
+ TAG_DEBUG (ctx->frame, " Installing cached value for %s: %s\n",
+ map ((array(string)) var, replace, ".", "..") * ".",
+ utils->format_short (settings[encoded_var]));
+ #endif
+ ctx->set_var (var[1..], settings[encoded_var], var[0]);
+ }
+ return nil;
+ }
+
+ mapping(string:mixed) _encode() {return settings;}
+ void _decode (mapping(string:mixed) saved) {settings = saved;}
+
+ MARK_OBJECT;
+ string _sprintf() {return "RXML.ScopeChange" + OBJ_COUNT;}
+ }
+
class CompiledError
//! A compiled-in error. Used when the parser handles an error, to get
//! the same behavior in the p-code.
5859:
throw (bt);
}
- mixed _encode()
+ array _encode()
{
return ({type, msg, current_var});
}
- void _decode (mixed v)
+ void _decode (array v)
{
[type, msg, current_var] = v;
}
6041:
string _sprintf() {return "RXML.PikeCompile" + OBJ_COUNT;}
}
- class StalePCode
- {
- constant is_RXML_StalePCode = 1;
- }
-
+
class PCode
//! Holds p-code and evaluates it. P-code is the intermediate form
//! after parsing and before evaluation.
6063:
//! The tag set (if any) used by the parser that created this
//! object. Used to initialize new contexts correctly.
- int generation;
- //! The generation of @[tag_set] when the p-code object was
- //! generated. It's only valid to reevaluate as long as the
- //! generation matches.
-
+
int recover_errors;
//! Nonzero if error recovery is allowed. Should be the same as the
//! setting in the parser used to create this object.
-
+ PCode p_code = 0;
+ //! Another chained PCode object to update while this one is
+ //! compiled or evaluated. Typically only useful if one p-code
+ //! object collects the unevaluated data and the other the results.
+ //! It's assumed that at most one PCode object in a chain collects
+ //! results.
+
Context new_context (void|RequestID id)
//! Creates a new context for evaluating the p-code in this object.
//! @[id] is put into the context if given.
{
Context ctx = tag_set ? tag_set->new_context (id) : Context (0, id);
- ctx->make_p_code = -1; // Always extend the compilation to unvisited parts.
+ ctx->make_p_code = 1; // Always extend the compilation to unvisited parts.
return ctx;
}
-
+ int is_stale()
+ //! Returns whether the p-code is stale or not. Should be called
+ //! before @[eval] to ensure it won't fail for that reason.
+ {
+ return tag_set && tag_set->generation != generation;
+ }
+
mixed eval (Context context, void|int eval_piece)
//! Evaluates the p-code in the given context (which typically has
//! been created by @[new_context]). If @[eval_piece] is nonzero,
6088:
//! streaming/nonblocking operation. @[context->incomplete_eval]
//! will return nonzero in that case.
//!
- //! This function returns an instance of @[StalePCode] if the p-code
- //! is stale, i.e. if @[generation] no longer matches the generation
- //! of @[tag_set]. The caller should in that case evaluate from the
- //! source instead.
+ //! This function might throw an exception if the p-code is stale,
+ //! i.e. if @[generation] no longer matches the generation of
+ //! @[tag_set]. The caller should always check with @[is_stale] to
+ //! avoid that.
{
mixed res, piece;
int eval_loop = 0;
6111:
}
piece = _eval (context); // Might unwind.
}) {
- if (objectp (err))
- if (([object] err)->thrown_at_unwind) {
+ if (objectp (err) && ([object] err)->thrown_at_unwind) {
if (!eval_piece && context->incomplete_eval()) {
if (eval_loop) res = add_to_value (type, res, piece);
eval_loop = 1;
6122:
context->unwind_state->top = this_object();
break eval;
}
- else if (([object] err)->is_RXML_StalePCode) {
- piece = err;
- break eval;
- }
+
LEAVE_CONTEXT();
throw_fatal (err);
}
6137:
return piece;
}
- //function(Context:mixed) compile();
- // Returns a compiled function for doing the evaluation. The
- // function will receive a context to do the evaluation in.
-
- static void create (Type _type, void|TagSet _tag_set)
+ static void create (Type _type, void|TagSet _tag_set, void|Context collect_results)
{
if (_type) { // 0 if we're being decoded.
type = _type;
if ((tag_set = _tag_set)) generation = _tag_set->generation;
- p_code = allocate (16);
+ exec = allocate (16);
}
-
+ if (collect_results) {
+ // Yes, the internal interaction between create, reset, the
+ // context and CTX_ALREADY_GOT_VC is ugly.
+ flags = COLLECT_RESULTS;
+ if (collect_results->misc->variable_changes) flags |= CTX_ALREADY_GOT_VC;
+ collect_results->misc->variable_changes = ([]);
}
-
+ }
// Internals:
- static array p_code = 0;
+ static array exec = 0;
static int length = 0;
- static int fully_resolved = 0;
+
-
+ static int flags = 0;
+ static constant FULLY_RESOLVED = 0x1;
+ static constant COLLECT_RESULTS = 0x2;
+ static constant CTX_ALREADY_GOT_VC = 0x4; // Just as ugly as it sounds, but who cares?
+
+ static int generation;
+ // The generation of tag_set when the p-code object was generated.
+ // Known punt: We should track and check the generations of any
+ // nested tag sets so that is_stale always is reliable. But due to
+ // the extensive dependencies in the global rxml_tag_set that won't
+ // be a problem in practice, so we avoid the overhead.
+
void reset (Type _type, void|TagSet _tag_set)
{
type = _type;
if ((tag_set = _tag_set)) generation = _tag_set->generation;
- p_code = allocate (16);
+ exec = allocate (16);
length = 0;
-
+ flags &= ~FULLY_RESOLVED;
+ if (p_code) p_code->reset (_type, _tag_set);
}
- void add (mixed entry)
+ void add (Context ctx, mixed entry, mixed evaled_value)
{
- if (length >= sizeof (p_code)) p_code += allocate (sizeof (p_code));
- p_code[length++] = entry;
+ if (length + 1 > sizeof (exec)) exec += allocate (sizeof (exec));
+
+ if (flags & COLLECT_RESULTS) {
+ exec[length++] = evaled_value;
+ mapping(string:mixed) var_chg = ctx->misc->variable_changes;
+ if (sizeof (var_chg)) {
+ exec[length++] = ScopeChange (var_chg);
+ ctx->misc->variable_changes = ([]);
}
-
+ }
+ else
+ exec[length++] = entry;
-
+ if (p_code) p_code->add (ctx, entry, evaled_value);
+ }
+
#define RESET_FRAME(frame) do { \
frame->result = nil; \
frame->up = 0; \
/* Maybe zap vars too, if it exists? */ \
} while (0)
- void add_frame (Frame frame, Context ctx)
+ void add_frame (Context ctx, Frame frame, mixed evaled_value)
{
- if (length + 3 > sizeof (p_code)) p_code += allocate (sizeof (p_code));
- p_code[length] = frame->tag || frame; // To make new frames from.
+ add_frame:
+ {
+ add_evaled_value:
+ if (flags & COLLECT_RESULTS && !(frame->flags & FLAG_DONT_CACHE_RESULT)) {
+ if (evaled_value == PCode) {
+ // The PCode value is only used as an ugly magic cookie to
+ // signify that the frame produced no result to add (i.e. it
+ // threw an exception instead). In that case we must keep
+ // the frame unevaluated.
+ Frame f = frame;
+ do
+ f->flags |= FLAG_DONT_CACHE_RESULT;
+ while ((f = f->up) && !(f->flags & FLAG_DONT_CACHE_RESULT));
+ break add_evaled_value;
+ }
+ if (length + 1 >= sizeof (exec)) exec += allocate (sizeof (exec));
+ exec[length++] = evaled_value;
+ mapping(string:mixed) var_chg = ctx->misc->variable_changes;
+ if (sizeof (var_chg)) {
+ exec[length++] = ScopeChange (var_chg);
+ ctx->misc->variable_changes = ([]);
+ }
+ break add_frame;
+ }
+
+ if (length + 3 > sizeof (exec)) exec += allocate (sizeof (exec));
+ exec[length] = frame->tag || frame; // To make new frames from.
#ifdef DEBUG
if (!stringp (frame->args) && !functionp (frame->args) && !mappingp (frame->args))
error ("Invalid args %s in frame about to be added to p-code.\n",
utils->format_short (frame->args));
#endif
- p_code[length + 1] = frame; // Cached for reuse.
- array frame_state = p_code[length + 2] = frame->_save();
+ exec[length + 1] = frame; // Cached for reuse.
+ array frame_state = exec[length + 2] = frame->_save();
if (stringp (frame_state[0]))
ctx->p_code_comp->delayed_resolve (frame_state, 0);
RESET_FRAME (frame);
length += 3;
}
-
+ if (p_code) p_code->add_frame (ctx, frame, evaled_value);
+ }
+
void finish()
{
- if (length != sizeof (p_code))
- p_code = p_code[..length - 1];
+ if (length != sizeof (exec)) exec = exec[..length - 1];
+ if (p_code) p_code->finish();
+ if ((flags & (COLLECT_RESULTS|CTX_ALREADY_GOT_VC)) == COLLECT_RESULTS)
+ m_delete (RXML_CONTEXT->misc, "variable_changes");
}
mixed _eval (Context ctx)
6208:
array parts;
int ppos = 0;
- if (tag_set && tag_set->generation != generation) throw (StalePCode());
+ #ifdef MODULE_DEBUG
+ if (tag_set && tag_set->generation != generation) error ("P-code is stale.\n");
+ #endif
if (ctx->unwind_state)
[object ignored, pos, parts, ppos] =
6216:
else parts = allocate (length);
while (1) { // Loops only if errors are catched.
+ mixed item;
if (mixed err = catch {
#define EVAL_LOOP(RESOLVE_ARGFUNC_1, RESOLVE_ARGFUNC_2) \
do for (; pos < length; pos++) { \
- mixed item = p_code[pos]; \
+ item = exec[pos]; \
+ chained_p_code_add: { \
if (objectp (item)) \
if (item->is_RXML_p_code_frame) { \
Frame frame; \
- if ((frame = p_code[pos + 1])) { \
+ if ((frame = exec[pos + 1])) { \
/* Relying on the interpreter lock here. */ \
- p_code[pos + 1] = 0; \
+ exec[pos + 1] = 0; \
RESOLVE_ARGFUNC_1; \
- } \
+ } \
else { \
- if (item->is_RXML_Tag) \
+ if (item->is_RXML_Tag) \
frame = item->Frame(), frame->tag = item; \
else frame = item->_clone_empty(); \
RESOLVE_ARGFUNC_2; \
- frame->_restore (p_code[pos + 2]); \
- } \
+ frame->_restore (exec[pos + 2]); \
+ } \
item = frame->_eval ( \
- ctx, this_object(), type); /* Might unwind. */ \
- if (!p_code[pos + 1]) { \
+ ctx, this_object(), type); /* Might unwind. */ \
+ if (!exec[pos + 1]) { \
RESET_FRAME (frame); \
/* Race here, but it doesn't matter much. */ \
- p_code[pos + 1] = frame; \
+ exec[pos + 1] = frame; \
+ } \
+ pos += 2; \
+ if (p_code) p_code->add_frame (ctx, frame, item); \
+ break chained_p_code_add; \
} \
- pos += 2; \
- } \
+
else if (item->is_RXML_p_code_entry) \
item = item->get (ctx); /* Might unwind. */ \
-
+ if (p_code) p_code->add (ctx, item, item); \
+ } \
if (item != nil) \
parts[ppos++] = item; \
if (string errmsgs = m_delete (ctx->misc, this_object())) \
parts[ppos++] = errmsgs; \
} while (0)
- if (fully_resolved)
+ if (flags & FULLY_RESOLVED)
EVAL_LOOP (;, ;);
else
EVAL_LOOP ({
- array frame_state = p_code[pos + 2];
+ array frame_state = exec[pos + 2];
if (stringp (frame->args))
if (stringp (frame_state[0]))
frame->args = frame_state[0] =
6265:
else
frame->args = frame_state[0];
}, {
- array frame_state = p_code[pos + 2];
+ array frame_state = exec[pos + 2];
if (stringp (frame_state[0]))
frame_state[0] = ctx->p_code_comp->resolve (frame_state[0]);
});
6287:
throw (this_object());
}
else {
+ if (objectp (item) && item->is_RXML_p_code_frame) {
+ // If the exception comes from a frame, handle_exception
+ // already has been called. Thus we don't try to recover
+ // again.
+ if (p_code) p_code->add_frame (ctx, item, PCode);
+ }
+ else {
+ if (p_code) p_code->add (ctx, item, item);
+ err = catch {
ctx->handle_exception (err, this_object()); // May throw.
string msgs = m_delete (ctx->misc, this_object());
if (pos >= length)
return msgs || nil;
else {
if (msgs) parts[ppos++] = msgs;
- if (objectp (p_code[pos]) && p_code[pos]->is_RXML_p_code_frame)
+ if (objectp (exec[pos]) && exec[pos]->is_RXML_p_code_frame)
pos += 3;
else
pos++;
continue;
}
-
+ };
}
-
+ if (tag_set && tag_set->generation != generation)
+ catch (err[0] += "Note: Error happened in stale p-code.\n");
+ throw (err);
+ }
error ("Should not get here.\n");
}
6312:
//! in a variable named @tt{ctx@} and the parent evaluator in
//! @tt{evaler@}. It also assumes there's a mixed variable called
//! @tt{tmp@} for temporary use. No code is added to handle
- //! exception unwinding and rewinding or checks for staleness.
- //! Mostly for internal use.
+ //! exception unwinding and rewinding, checks for staleness or
+ //! chained p-code. Mostly for internal use.
{
if (!length)
return type->sequential ? comp->bind (type->empty_value) : "RXML.nil";
6322:
array(string) parts = allocate (length);
for (int pos = 0; pos < length; pos++) {
- mixed item = p_code[pos];
+ mixed item = exec[pos];
if (objectp (item))
if (item->is_RXML_p_code_frame) {
// NB: We currently don't use the cached frame in
- // p_code[pos+1] here.
- string|EVAL_ARGS_FUNC argfunc = p_code[pos + 2][0];
+ // exec[pos+1] here.
+ string|EVAL_ARGS_FUNC argfunc = exec[pos + 2][0];
if (stringp (argfunc))
// It's possible to delay this by adding code to set the
// argfunc slot below.
- p_code[pos + 2][0] = comp->resolve (argfunc);
+ exec[pos + 2][0] = comp->resolve (argfunc);
parts[pos] = sprintf (
(item->is_RXML_Tag ?
"(tmp=%s.Frame(),tmp->tag=%[0]s," : "(tmp=%s._clone_empty(),") +
"tmp->_restore(%s),"
"tmp->_eval(ctx,evaler,%s))",
comp->bind (item),
- comp->bind (p_code[pos + 2]),
+ comp->bind (exec[pos + 2]),
typevar);
pos += 2;
continue;
6347:
parts[pos] = sprintf ("%s.get(ctx)", comp->bind (item));
continue;
}
- parts[pos] = comp->bind (p_code[pos]);
+ parts[pos] = comp->bind (exec[pos]);
}
if (type->sequential)
6376:
mixed _encode()
{
- finish();
- array encode_p_code = p_code + ({});
+ if (length != sizeof (exec)) exec = exec[..length - 1];
+ array encode_p_code = exec + ({});
for (int pos = 0; pos < length; pos++) {
mixed item = encode_p_code[pos];
if (objectp (item) && item->is_RXML_p_code_frame) {
6387: Inside #if defined(DEBUG)
error ("Unresolved argument function in frame at position %d.\n"
"Encoding p-code in unfinished evaluation?\n", pos);
#endif
- if (p_code[pos + 1]) p_code[pos + 1]->args = encode_p_code[pos + 2][0];
+ if (exec[pos + 1]) exec[pos + 1]->args = encode_p_code[pos + 2][0];
}
}
- fully_resolved = 1;
+ flags |= FULLY_RESOLVED;
return ({tag_set, tag_set && tag_set->get_hash(),
type, recover_errors, encode_p_code});
6398:
void _decode(array v)
{
- [tag_set, string tag_set_hash, type, recover_errors, p_code] = v;
- length = sizeof (p_code);
+ [tag_set, string tag_set_hash, type, recover_errors, exec] = v;
+ length = sizeof (exec);
if (tag_set) {
if (tag_set->get_hash() != tag_set_hash)
error ("P-code is stale; the tag set has changed since it was encoded.\n");
generation = tag_set->generation;
}
- fully_resolved = 1;
+ flags |= FULLY_RESOLVED;
// Instantiate the cached frames, mainly so that any errors in
// their restore functions due to old data are triggered here and
// not later during evaluation.
for (int pos = 0; pos < length; pos++) {
- mixed item = p_code[pos];
+ mixed item = exec[pos];
if (objectp (item) && item->is_RXML_p_code_frame) {
Frame frame;
if (item->is_RXML_Tag)
- p_code[pos + 1] = frame = item->Frame(), frame->tag = item;
+ exec[pos + 1] = frame = item->Frame(), frame->tag = item;
else
- p_code[pos + 1] = frame = item->_clone_empty();
- frame->_restore (p_code[pos + 2]);
+ exec[pos + 1] = frame = item->_clone_empty();
+ frame->_restore (exec[pos + 2]);
pos += 2;
}
}
6434:
string source;
//! The source code or frame used to generate the p-code.
+ int is_stale() {return 0;}
-
+
// Internals:
mixed _eval (Context ctx)
{
-
+ if (::is_stale()) {
Parser parser = 0;
-
+
if (ctx->unwind_state)
[parser] = m_delete (ctx->unwind_state, this_object());
- else {
- mixed err = catch {
- return ::_eval (ctx);
- };
- if (!objectp (err) || !err->is_RXML_StalePCode)
- throw (err);
- }
+
mixed res;
if (mixed err = catch {
if (!parser) {
- ctx->init_make_p_code();
+
parser = type->get_parser (ctx, tag_set, 0, this_object());
parser->finish (source); // Might unwind.
}
6470:
type->give_back (parser, tag_set);
return res;
}
+ else
+ return ::_eval (ctx);
+ }
array _encode()
{
6569:
error ("Cannot find parser %O.\n", parser_name);
ENCODE_DEBUG_RETURN (reg_types[what[1]] (parser_prog, @what[3..]));
}
- case "ObjectMarker":
+
#ifdef RXML_OBJ_DEBUG
-
+ case "ObjectMarker":
ENCODE_DEBUG_RETURN (Debug.ObjectMarker (what[1]));
- #else
- // This at least avoids the debug printouts if an entry
- // encoded with RXML_OBJ_DEBUG is decoded.
- ENCODE_DEBUG_RETURN (Debug.ObjectMarker (0));
+
#endif
}
}