2001-06-09
2001-06-09 00:33:25 by Martin Stjernholm <mast@lysator.liu.se>
-
a08cd7c234ee0c8b46e7a0bd0a27868feefa0e86
(1328 lines)
(+875/-453)
[
Show
| Annotate
]
Branch: 5.2
Implemented PCode compilation and evaluation. Known bugs: RXML error
backtraces might be duplicated since they're stored as text in the compiled
code. Splice args doesn't yet work together with raw_tag_text.
Rev: server/etc/modules/RXML.pmod/PXml.pike:1.56
Rev: server/etc/modules/RXML.pmod/module.pmod:1.162
Rev: server/etc/modules/RXML.pmod/utils.pmod:1.23
1:
- // $Id: module.pmod,v 1.161 2001/06/01 17:25:40 jhs Exp $
+ // $Id: module.pmod,v 1.162 2001/06/09 00:33:24 mast Exp $
// Kludge: Must use "RXML.refs" somewhere for the whole module to be
// loaded correctly.
65:
#include <request_trace.h>
+ #define MAGIC_HELP_ARG
+ // #define OBJ_COUNT_DEBUG
+ // #define RXML_VERBOSE
// #define PROFILE_PARSER
148:
// Use defines since typedefs doesn't work in soft casts yet.
#define SCOPE_TYPE mapping(string:mixed)|object(Scope)
#define UNWIND_STATE mapping(string|object:mixed|array)
+ #define EVAL_ARGS_FUNC function(Context:mapping(string:mixed))
class Tag
278:
//! occurs. @[ctx] specifies the context to use; it defaults to the
//! current context.
{
- // Note: Code duplication in Frame._eval().
+ // Note: Code duplication in Frame._eval_args and Frame._prepare.
mapping(string:Type) atypes = args & req_arg_types;
if (sizeof (atypes) < sizeof (req_arg_types))
if (dont_throw) return 0;
293: Inside #if defined(MODULE_DEBUG)
#ifdef MODULE_DEBUG
if (mixed err = catch {
#endif
- foreach (indices (atypes), string arg)
- args[arg] = atypes[arg]->eval (args[arg], ctx); // Should not unwind.
+ foreach (indices (args), string arg)
+ args[arg] = (atypes[arg] || def_arg_type)->eval (
+ args[arg], ctx); // Should not unwind.
#ifdef MODULE_DEBUG
}) {
if (objectp (err) && ([object] err)->thrown_at_unwind)
307:
//(!) Internals:
- #define MAKE_FRAME(frame, ctx, parser, args) \
+ #define MAKE_FRAME(_frame, _ctx, _parser, _args) \
make_new_frame: do { \
- 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; \
+ 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; \
break make_new_frame; \
} \
- frame = `() (args, nil); \
+ _frame = `() (0, nil); \
DO_IF_DEBUG( \
- if (args && ([mapping] args)["-debug-tag-"]) { \
- frame->flags |= FLAG_DEBUG; \
- m_delete (args, "-debug-tag-"); \
+ if (_args && ([mapping] (mixed) _args)["-debug-tag-"]) { \
+ _frame->flags |= FLAG_DEBUG; \
+ m_delete (_args, "-debug-tag-"); \
} \
); \
- TAG_DEBUG (frame, "New frame\n"); \
+ TAG_DEBUG (_frame, "New frame\n"); \
} while (0)
- #define EVAL_FRAME(frame, ctx, parser, type, args, content, result) \
+ #define EVAL_FRAME(_frame, _ctx, _parser, _type, _args, _content, _res) \
eval_frame: do { \
mixed err = catch { \
- result = frame->_eval (ctx, parser, type, args, content); \
+ EVAL_ARGS_FUNC argfunc; \
+ if (!_frame->args) \
+ argfunc = _frame->_prepare (_ctx, _type, _args); \
+ _res = _frame->_eval (_ctx, _parser, _type, _content || ""); \
+ if (_parser->p_code) { \
+ _frame->args = argfunc; \
+ _parser->p_code->add (_frame); \
+ } \
break eval_frame; \
}; \
\
if (objectp (err) && ([object] err)->thrown_at_unwind) { \
- UNWIND_STATE ustate = ctx->unwind_state; \
- if (!ustate) ustate = ctx->unwind_state = ([]); \
+ UNWIND_STATE ustate = _ctx->unwind_state; \
+ if (!ustate) ustate = _ctx->unwind_state = ([]); \
DO_IF_DEBUG ( \
- if (err != frame) \
+ if (err != _frame) \
fatal_error ("Unexpected unwind object catched.\n"); \
- if (ustate[parser]) \
+ if (ustate[_parser]) \
fatal_error ("Clobbering unwind state for parser.\n"); \
); \
- ustate[parser] = ({err}); \
- throw (err = parser); \
+ ustate[_parser] = ({_frame}); \
+ throw (_parser); \
} \
else { \
/* Will rethrow unknown errors. */ \
- ctx->handle_exception (err, parser); \
- result = nil; \
+ _ctx->handle_exception (err, _parser); \
+ _res = nil; \
} \
} while (0)
370:
mixed result;
EVAL_FRAME (frame, ctx, parser, parser->type, args, content, result);
- if (parser->p_code) parser->p_code->add (frame);
+
return result;
}
383:
Context ctx = parser->context;
- string splice_args;
- if (args && (splice_args = args["::"])) {
- #ifdef MODULE_DEBUG
- if (mixed err = catch {
- #endif
- splice_args = t_string (PEnt)->eval (splice_args, ctx, 0, parser, 1);
- #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
- m_delete (args, "::");
- args += parser->parse_tag_args (splice_args);
- }
-
+
object/*(Frame)HMM*/ frame;
MAKE_FRAME (frame, ctx, parser, args);
if (!zero_type (frame->raw_tag_text))
- if (splice_args)
- frame->raw_tag_text =
- t_xml->format_tag (parser->tag_name(), args, content, flags | FLAG_RAW_ARGS);
- else frame->raw_tag_text = parser->current_input();
+ frame->raw_tag_text = parser->current_input();
mixed result;
EVAL_FRAME (frame, ctx, parser, type, args, content, result);
- if (parser->p_code) {
- parser->p_code->add (frame);
- if (result != nil) parser->add_value (result);
- }
- else if (result != nil) {
- if (type->free_text) return ({result});
+ if (result != nil) {
+ if (type->free_text && !parser->p_code) return ({result});
parser->add_value (result);
}
return ({});
449:
mixed result;
EVAL_FRAME (frame, ctx, parser, type, 0, content, result);
- if (parser->p_code) {
- parser->p_code->add (frame);
- if (result != nil) parser->add_value (result);
- }
- else if (result != nil) {
- if (type->free_text) return ({result});
+ if (result != nil) {
+ if (type->free_text && !parser->p_code) return ({result});
parser->add_value (result);
}
return ({});
1211:
//! 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).
+ //!
+ //! @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].
{
if(!var || !sizeof(var)) return ([])[0];
array(string|int) splitted = parse_user_var (var, scope_name);
1270:
//! 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).
+ //!
+ //! @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].
{
if(!var || !sizeof(var)) parse_error ("No variable specified.\n");
array(string|int) splitted = parse_user_var (var, scope_name);
1319:
//! 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).
+ //!
+ //! @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].
{
if(!var || !sizeof(var)) return;
array(string|int) splitted = parse_user_var (var, scope_name);
1587:
// do_process() with this stream piece.)
// "exec_left": array (Exec array left to evaluate. Only used
// between Frame._exec_array() and Frame._eval().)
- // "p_code_pos": int (The position in the p-code array, when a PCode
- // object is used for the evaluation.)
+
MARK_OBJECT_ONLY;
1687:
string describe_rxml_backtrace (void|int no_msg)
//! Returns a formatted RXML frame backtrace.
{
- string txt = no_msg ? "" : "RXML" + (type ? " " + type : "") + " error";
+ String.Buffer txt = String.Buffer();
+ txt->add (no_msg ? "" : "RXML" + (type ? " " + type : "") + " error");
if (context) {
- if (!no_msg) txt += ": " + (msg || "(no error message)\n");
- txt += current_var ? " | &" + current_var + ";\n" : "";
+ if (!no_msg) txt->add (": " + (msg || "(no error message)\n"));
+ if (current_var) txt->add (" | &" + current_var + ";\n");
for (Frame f = frame; f; f = f->up) {
string name;
if (f->tag) name = f->tag->name;
else if (!f->up) break;
else name = "(unknown)";
if (f->flags & FLAG_PROC_INSTR)
- txt += " | <?" + name + "?>\n";
+ txt->add (" | <?" + name + "?>\n");
else {
- txt += " | <" + name;
- if (f->args)
+ txt->add (" | <" + name);
+ if (mappingp (f->args))
foreach (sort (indices (f->args)), string arg) {
mixed val = f->args[arg];
- txt += " " + arg + "=";
- if (arrayp (val)) txt += map (val, error_print_val) * ",";
- else txt += error_print_val (val);
+ txt->add (" " + arg + "=");
+ if (arrayp (val)) txt->add (map (val, error_print_val) * ",");
+ else txt->add (error_print_val (val));
}
- else txt += " (no argmap)";
- txt += ">\n";
+ else txt->add (" (no argmap)");
+ txt->add (">\n");
}
}
}
else
- if (!no_msg) txt += " (no context): " + (msg || "(no error message)\n");
- return txt;
+ if (!no_msg) txt->add (" (no context): " + (msg || "(no error message)\n"));
+ return txt->get();
}
private string error_print_val (mixed val)
1952:
//! constants. It's copied from @[Tag.flag] when the frame is
//! created.
- mapping(string:mixed) args;
+ 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. Not set for processing instruction (@[FLAG_PROC_INSTR])
//! tags.
-
+ //!
+ //! This variable is also used to hold a function that generates the
+ //! argument mapping between evaluations of the frame. It never does
+ //! when any of the callbacks except @[cached_return] are called,
+ //! though.
Type content_type;
//! The type of the content.
- mixed content = nil;
+ mixed|PCode content = nil;
//! The content, if any. Set before @[do_process] and @[do_return]
//! are called. Initialized to @[RXML.nil] every time the frame
//! executed.
-
+ //!
+ //! This variable is also used to hold an unevaluated representation
+ //! of the content between evaluations of the frame. It never does
+ //! when any of the callbacks except @[cached_return] are called,
+ //! though.
Type result_type;
//! The required result type. If it has a parser, it will affect how
1989:
//! contains the value after any parsing and will not be parsed
//! again.
- //! @decl mapping(string:mixed) vars;
+ //! @decl optional mapping(string:mixed) vars;
//!
//! Set this to introduce a new variable scope that will be active
//! during parsing of the content and return values (but see also
//! @[FLAG_PARENT_SCOPE]).
- //! @decl string scope_name;
+ //! @decl optional string scope_name;
//!
//! 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.
- //! @decl TagSet additional_tags;
+ //! @decl optional TagSet additional_tags;
//!
//! 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.
- //! @decl TagSet local_tags;
+ //! @decl optional TagSet local_tags;
//!
//! 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.
- //! @decl Frame parent_frame;
+ //! @decl optional Frame parent_frame;
//!
//! If this variable exists, it gets set to the frame object of the
//! closest surrounding tag that defined this tag in its
//! @[additional_tags] or @[local_tags]. Useful to access the
//! "mother tag" from the subtags it defines.
- //! @decl string raw_tag_text;
+ //! @decl optional string raw_tag_text;
//!
//! 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.
- //! @decl array do_enter (RequestID id);
- //! @decl array do_process (RequestID id, void|mixed piece);
- //! @decl array do_return (RequestID id);
+ //! @decl optional array do_enter (RequestID id);
+ //! @decl optional array do_process (RequestID id, void|mixed piece);
+ //! @decl optional array do_return (RequestID id);
//!
//! @[do_enter] is called first thing when processing the tag.
//! @[do_process] is called after (some of) the content has been
2061:
//! content will be parsed. It's result is added or put into the
//! result of this tag.
//! @item mapping(string:mixed)
- //! Fields to merge into the headers. FIXME: Not yet
- //! implemented.
+ //! A response mapping which will be returned instead of the
+ //! evaluated page. The evaluation is stopped immediately after
+ //! this. FIXME: Not yet implemented.
//! @item object
//! Treated as a file object to read in blocking or nonblocking
//! mode. FIXME: Not yet implemented, details not decided.
2116:
//! from the execution array that is propagated after each turn; the
//! result variable only accumulates all these pieces.
- //! @decl int do_iterate (RequestID id);
+ //! @decl optional int do_iterate (RequestID id);
//!
//! 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
2136:
//! 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.
- //! @decl int|function(RequestID:int) is_valid;
+ //! @decl optional int|function(RequestID:int) is_valid;
//!
//! When defined, the frame may be cached. First the name of the tag
//! must be the same. Then the conditions specified by the cache
2247:
//! used in the tag after parsing, otherwise the @[raw_tag_text]
//! variable is used, which must have a string value.
{
+ Frame this = this_object();
#ifdef MODULE_DEBUG
#define CHECK_RAW_TEXT \
- if (zero_type (this_object()->raw_tag_text)) \
+ if (zero_type (this->raw_tag_text)) \
fatal_error ("The variable raw_tag_text must be defined.\n"); \
- if (!stringp (this_object()->raw_tag_text)) \
+ if (!stringp (this->raw_tag_text)) \
fatal_error ("raw_tag_text must have a string value.\n");
#else
#define CHECK_RAW_TEXT
2270:
sscanf (c, "%*[^ \t\n\r]%s", content);
throw (0);
},
- "?")->finish (this_object()->raw_tag_text);
+ "?")->finish (this->raw_tag_text);
}) throw (err);
#ifdef DEBUG
if (!stringp (content))
fatal_error ("Failed to parse PI tag content for <?%s?> from %O.\n",
- tag->name, this_object()->raw_tag_text);
+ tag->name, this->raw_tag_text);
#endif
}
}
2296:
});
return 1;
}
- })->finish (this_object()->raw_tag_text);
+ })->finish (this->raw_tag_text);
}) throw (err);
#ifdef DEBUG
if (!mappingp (args))
fatal_error ("Failed to parse tag args for <%s> from %O.\n",
- tag->name, this_object()->raw_tag_text);
+ tag->name, this->raw_tag_text);
if (!stringp (content) && !(flags & FLAG_EMPTY_ELEMENT))
fatal_error ("Failed to parse tag content for <%s> from %O.\n",
- tag->name, this_object()->raw_tag_text);
+ tag->name, this->raw_tag_text);
#endif
}
2323:
return result_type->format_tag (tag, 0, content);
else {
CHECK_RAW_TEXT;
- return this_object()->raw_tag_text;
+ return this->raw_tag_text;
}
}
2337: Inside #if defined(MODULE_DEBUG)
#ifdef MODULE_DEBUG
if (mixed err = catch {
#endif
- s = t_xml (PEnt)->eval (this_object()->raw_tag_text,
+ s = t_xml (PEnt)->eval (this->raw_tag_text,
get_context(), empty_tag_set);
#ifdef MODULE_DEBUG
}) {
2362:
});
return 1;
}
- })->finish (this_object()->raw_tag_text);
+ })->finish (this->raw_tag_text);
}) throw (err);
#ifdef DEBUG
if (!mappingp (args))
fatal_error ("Failed to parse tag args for <%s> from %O.\n",
- tag->name, this_object()->raw_tag_text);
+ tag->name, this->raw_tag_text);
if (!stringp (content))
fatal_error ("Failed to parse tag content for <%s> from %O.\n",
- tag->name, this_object()->raw_tag_text);
+ tag->name, this->raw_tag_text);
#endif
return result_type->format_tag (tag, args, content);
}
2431:
else to = from; \
} while (0)
- mixed _exec_array (Context ctx, TagSetParser|PCode evaler, array exec)
+ 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)
+ {
Frame this = this_object();
int i = 0, parent_scope = flags & FLAG_PARENT_SCOPE;
mixed res = nil;
2466:
break;
case "mapping":
- THIS_TAG_DEBUG ("Exec[%d]: Header mapping\n", i);
- fatal_error ("Header mappings not yet implemented.\n");
+ THIS_TAG_DEBUG ("Exec[%d]: Response mapping\n", i);
+ _exec_array_fatal (where, i, elem,
+ "Response mappings not yet implemented.\n");
break;
case "multiset":
2476:
THIS_TAG_DEBUG ("Exec[%d]: Verbatim value %s\n", i,
utils->format_short (piece));
}
- else if (sizeof ([multiset] elem) > 1)
- fatal_error ("%d values in multiset in position %d in exec array.\n",
- sizeof ([multiset] elem), i);
- else fatal_error ("No value in multiset in position %d in exec array.\n", i);
+ else
+ _exec_array_fatal (where, i, elem,
+ "Not exactly one value in multiset.\n");
break;
default:
2489:
if (([object] elem)->is_RXML_Frame) {
THIS_TAG_DEBUG ("Exec[%d]: Evaluating frame %O\n", i, ([object] elem));
piece = ([object(Frame)] elem)->_eval (
- ctx, evaler, result_type, 0, 0); // Might unwind.
+ ctx, evaler, result_type); // Might unwind.
+ break;
}
else if (([object] elem)->is_RXML_Parser) {
// The subparser above unwound.
2497:
i, ([object] elem));
([object(Parser)] elem)->finish(); // Might unwind.
piece = ([object(Parser)] elem)->eval(); // Might unwind.
+ break;
}
- else
- fatal_error ("File objects not yet implemented.\n");
- else
- fatal_error ("Invalid type %t in position %d in exec array.\n", elem, i);
+ _exec_array_fatal (where, i, elem, "Not a valid type.\n");
}
if (result_type->sequential) SET_SEQUENTIAL (piece, res, "result");
2578:
THIS_TAG_DEBUG ("Calling " #cb "\n"); \
PROFILE_SWITCH (ctx, "rxml internal", "tag:" + tag->name); \
COND_PROF_ENTER(tag,tag->name,"tag"); \
- res = (cb) (args); /* Might unwind. */ \
+ res = (cb) (args); /* Might unwind. */ \
COND_PROF_LEAVE(tag,tag->name,"tag"); \
PROFILE_SWITCH (ctx, "tag:" + tag->name, "rxml internal"); \
} while (0)
2608:
#define EXEC_ARRAY(ctx, evaler, exec, cb) \
do { \
if (exec) { \
- mixed res = _exec_array (ctx, evaler, exec); /* Might unwind. */ \
+ mixed res = \
+ _exec_array (ctx, evaler, exec, #cb); /* Might unwind. */ \
if (flags & FLAG_STREAM_RESULT) { \
DO_IF_DEBUG ( \
if (ctx->unwind_state) \
2627:
} \
} while (0)
- mixed _eval (Context ctx, TagSetParser|PCode evaler, Type type,
- mapping(string:string) raw_args, string raw_content)
- // Note: It might be somewhat tricky to override this function,
- // since it handles unwinding through exceptions. Note: Might be
- // destructive on raw_args.
+ private mapping(string:mixed) _eval_args (Context ctx,
+ mapping(string:string) raw_args,
+ mapping(string:Type) my_req_args)
+ // Used for evaluating the dynamic arguments in the splice argument.
+ // Destructive on raw_args.
{
- Frame this = this_object();
- RequestID id = ctx->id;
+ // Note: Approximate code duplication in _prepare and Tag.eval_args().
+ mapping(string:Type) atypes =
+ raw_args & (tag->req_arg_types | tag->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");
+ }
- // Unwind state data:
- //raw_content
- #define EVSTAT_BEGIN 0
- #define EVSTAT_ENTERED 1
- #define EVSTAT_LAST_ITER 2
- #define EVSTAT_ITER_DONE 3
- int eval_state = EVSTAT_BEGIN;
- int iter;
- #ifdef DEBUG
- int debug_iter = 1;
- #endif
- Parser subparser;
- mixed piece;
- array exec = 0;
- TagSet orig_tag_set; // Flags that we added additional_tags to ctx->tag_set.
- //ctx->new_runtime_tags
-
- #define PRE_INIT_ERROR(X) (ctx->frame = this, fatal_error (X))
- #ifdef DEBUG
- // Internal sanity checks.
- if (ctx != get_context())
- PRE_INIT_ERROR ("Context not current.\n");
- if (!evaler->tag_set_eval)
- PRE_INIT_ERROR ("Calling _eval() with non-tag set parser.\n");
- #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");
+ if (mixed err = catch {
#endif
-
- if (array state = ctx->unwind_state && ctx->unwind_state[this]) {
- #ifdef DEBUG
- if (!up)
- PRE_INIT_ERROR ("Resuming frame without up pointer.\n");
- if (raw_args || raw_content)
- PRE_INIT_ERROR ("Can't feed new arguments or content when resuming parse.\n");
- #endif
- object ignored;
- [ignored, eval_state, iter, raw_content, subparser, piece, exec, orig_tag_set,
- ctx->new_runtime_tags
- #ifdef DEBUG
- , debug_iter
- #endif
- ] = state;
- m_delete (ctx->unwind_state, this);
- if (!sizeof (ctx->unwind_state)) ctx->unwind_state = 0;
- THIS_TAG_TOP_DEBUG ("Continuing evaluation" +
- (piece ? " with stream piece\n" : "\n"));
+ foreach (indices (raw_args), string arg) {
+ Type t = atypes[arg] || tag->def_arg_type;
+ if (t->parser_prog != PNone) {
+ Parser parser = t->get_parser (ctx, 0, 0);
+ THIS_TAG_DEBUG ("Evaluating argument value %s with %O\n",
+ utils->format_short (raw_args[arg]), parser);
+ parser->finish (raw_args[arg]); // Should not unwind.
+ raw_args[arg] = parser->eval(); // Should not unwind.
+ THIS_TAG_DEBUG ("Setting dynamic argument %s to %s\n",
+ utils->format_short (arg),
+ utils->format_short (raw_args[arg]));
+ t->give_back (parser);
}
- else {
- if (flags & FLAG_UNPARSED) {
- #ifdef DEBUG
- if (raw_args || raw_content)
- PRE_INIT_ERROR ("raw_args or raw_content given for unparsed frame.\n");
- #endif
- raw_args = args, args = 0;
- raw_content = content, content = nil;
+ }
#ifdef MODULE_DEBUG
- if (!stringp (raw_content))
- PRE_INIT_ERROR ("Content is not a string in unparsed tag frame.\n");
+ }) {
+ if (objectp (err) && ([object] err)->thrown_at_unwind)
+ fatal_error ("Can't save parser state when evaluating dynamic arguments.\n");
+ throw_fatal (err);
+ }
#endif
- THIS_TAG_TOP_DEBUG ("Evaluating unparsed\n");
+
+ return raw_args;
}
- else THIS_TAG_TOP_DEBUG ("Evaluating\n");
+
- #ifdef MODULE_DEBUG
- if (up && up != ctx->frame)
- PRE_INIT_ERROR ("Reuse of frame in different context.\n");
+ EVAL_ARGS_FUNC _prepare (Context ctx, Type type,
+ mapping(string:string) raw_args)
+ // 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.
+ {
+ Frame this = this_object();
+
+ mixed err = catch {
+ #ifdef DEBUG
+ if (!up)
#endif
up = ctx->frame;
- piece = nil;
+ ctx->frame = this; // Push the frame to get proper backtraces.
if (++ctx->frame_depth >= ctx->max_frame_depth) {
- ctx->frame = this;
+
ctx->frame_depth--;
_run_error ("Too deep recursion -- exceeding %d nested tags.\n",
ctx->max_frame_depth);
}
- }
+
- #undef PRE_INIT_ERROR
- ctx->frame = this;
- mixed conv_result = nil; // Result converted to the expected type.
- mixed err1 = 0;
- process_tag: {
- if ((err1 = catch {
- if (tag) {
- if ((raw_args || args || ([]))->help) {
- TRACE_ENTER ("tag <" + tag->name + " help>", 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);
- break process_tag;
- }
+ EVAL_ARGS_FUNC func;
- TRACE_ENTER("tag <" + tag->name + ">", 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");
- break process_tag;
- }
- #endif
- }
-
+
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)) {
- // Note: Code duplication in Tag.eval_args().
+ // Note: Approximate code duplication in _eval_args and Tag.eval_args().
+
+ string splice_arg = raw_args["::"];
+ if (splice_arg && type->entity_syntax)
+ // Note: Not really accurate to look at entity_syntax here.
+ m_delete (raw_args, "::");
+ 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 (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 ?
2760:
"argument " + missing[0] + " is") + " missing.\n");
}
atypes += raw_args & tag->opt_arg_types;
+
+ PikeCompile comp = PikeCompile();
+ String.Buffer fn_text = String.Buffer();
+
+ if (splice_arg) {
+ Parser p = splice_arg_type->get_pcode_parser (ctx, 0, 0);
+ THIS_TAG_DEBUG ("Evaluating splice argument %s\n",
+ utils->format_short (splice_arg));
#ifdef MODULE_DEBUG
if (mixed err = catch {
#endif
-
+ p->finish (splice_arg); // Should not unwind.
+ splice_arg = p->eval(); // Should not unwind.
+ #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
+ string expr =
+ sprintf ("%s (context, %s->parse_tag_args ((%s) || \"\"), %s)",
+ comp->bind (_eval_args),
+ comp->bind (xml_splice_arg_parser),
+ p->p_code->_compile_text (comp),
+ comp->bind (splice_req_types));
+ p->p_code = 0;
+ splice_arg_type->give_back (p);
+ args = _eval_args (
+ ctx, xml_splice_arg_parser->parse_tag_args (splice_arg || ""),
+ splice_req_types);
+
+ if (!zero_type (this->raw_tag_text)) {
+ // expr = sprintf (
+ // "mapping(string:string) args = %s;\n"
+ // "%s->raw_tag_text = %s->format_tag ("
+ // "%s, args, %s, %d | RXML.FLAG_RAW_ARGS);\n"
+ // "return args;",
+ // expr, comp->bind (this), comp->bind (type),
+ // comp->bind (tag->name), comp->bind (content), flags);
+ // frame->raw_tag_text =
+ // t_xml->format_tag (tagname, args, content, flags | FLAG_RAW_ARGS);
+ }
+ fn_text->add ("return ");
+ fn_text->add (expr);
+ fn_text->add (" + ([\n");
+ }
+ else {
+ args = raw_args;
+ fn_text->add ("return ([\n");
+ }
+
+ #ifdef MODULE_DEBUG
+ if (mixed err = catch {
+ #endif
foreach (indices (raw_args), string arg) {
- #ifdef DEBUG
+
Type t = atypes[arg] || tag->def_arg_type;
if (t->parser_prog != PNone) {
- Parser p = t->get_parser (ctx, 0, evaler);
- THIS_TAG_DEBUG ("Evaluating argument %s with %O\n",
- utils->format_short (arg), p);
- p->finish (raw_args[arg]); // Should not unwind.
- raw_args[arg] = p->eval(); // Should not unwind.
+ Parser parser = t->get_pcode_parser (ctx, 0, 0);
+ THIS_TAG_DEBUG ("Evaluating argument value %s with %O\n",
+ utils->format_short (raw_args[arg]), parser);
+ parser->finish (raw_args[arg]); // Should not unwind.
+ args[arg] = parser->eval(); // Should not unwind.
+ THIS_TAG_DEBUG ("Setting argument %s to %s\n",
+ utils->format_short (arg),
+ utils->format_short (args[arg]));
+ fn_text->add (sprintf ("%O: %s,\n", arg,
+ parser->p_code->_compile_text (comp)));
+ parser->p_code = 0;
+ t->give_back (parser);
}
- #else
- raw_args[arg] = (atypes[arg] || tag->def_arg_type)->
- eval (raw_args[arg], ctx, 0, evaler, 1); // Should not unwind.
- #endif
+ else {
+ args[arg] = raw_args[arg];
+ fn_text->add (sprintf ("%O: %s,\n", arg, comp->bind (raw_args[arg])));
}
-
+ }
#ifdef MODULE_DEBUG
}) {
if (objectp (err) && ([object] err)->thrown_at_unwind)
2785: Inside #if defined(MODULE_DEBUG)
throw_fatal (err);
}
#endif
+
+ fn_text->add ("]);\n");
+ string fn = comp->decl_func (
+ "mapping(string:mixed)", "RXML.Context context", fn_text->get());
+ func = comp->compile()()[fn];
}
-
+ else {
+ func = utils->return_empty_mapping;
args = raw_args;
}
- #ifdef MODULE_DEBUG
- else if (!args && !(flags & FLAG_PROC_INSTR)) fatal_error ("args not set.\n");
- #endif
+ }
+ else
+ func = utils->return_zero;
if (!zero_type (this->parent_frame))
if (up->local_tags && up->local_tags->has_tag (tag)) {
2810:
break;
}
- if (TagSet add_tags = raw_content && [object(TagSet)] this->additional_tags) {
- TagSet tset = ctx->tag_set;
- if (!tset->has_effective_tags (add_tags)) {
- THIS_TAG_DEBUG ("Installing additional_tags %O\n", add_tags);
- int hash = HASH_INT2 (tset->id_number, add_tags->id_number);
- orig_tag_set = tset;
- TagSet local_ts;
- if (!(local_ts = local_tag_set_cache[hash])) {
- local_ts = TagSet (add_tags->name + "+" + orig_tag_set->name);
- local_ts->imported = ({add_tags, orig_tag_set});
- local_tag_set_cache[hash] = local_ts; // Race, but it doesn't matter.
- }
- ctx->tag_set = local_ts;
- }
- else
- THIS_TAG_DEBUG ("Not installing additional_tags %O "
- "since they're already in the tag set\n", add_tags);
- }
-
+
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",
- this_object());
+ this);
#endif
find_result_type: {
// First check if any of the types is a subtype of the
2866: Inside #if defined(MODULE_DEBUG)
#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",
- this_object());
+ this);
#endif
content_type = tag->content_type;
if (content_type == t_same) {
2878:
}
else THIS_TAG_DEBUG ("Keeping content_type %O\n", content_type);
- if (raw_content) {
- THIS_TAG_DEBUG ("Initializing the content variable to nil\n");
+ ctx->frame = up;
+ return func;
+ };
+
+ ctx->frame = up;
+ throw (err);
+ }
+
+ mixed _eval (Context ctx, TagSetParser|PCode evaler, Type type,
+ void|string|PCode in_content)
+ // Note: It might be somewhat tricky to override this function,
+ // since it handles unwinding through exceptions.
+ {
+ Frame this = this_object();
+ RequestID id = ctx->id;
+
+ // Unwind state data:
+ #define EVSTAT_BEGIN 0
+ #define EVSTAT_ENTERED 1
+ #define EVSTAT_LAST_ITER 2
+ #define EVSTAT_ITER_DONE 3
+ int eval_state = EVSTAT_BEGIN;
+ EVAL_ARGS_FUNC in_args;
+ //in_content;
+ int iter;
+ #ifdef DEBUG
+ int debug_iter = 1;
+ #endif
+ object(Parser)|object(PCode) subevaler;
+ mixed piece;
+ array exec = 0;
+ TagSet orig_tag_set; // Flags that we added additional_tags to ctx->tag_set.
+ //ctx->new_runtime_tags
+
+ #define PRE_INIT_ERROR(X...) (ctx->frame = this, fatal_error (X))
+ #ifdef DEBUG
+ // Internal sanity checks.
+ if (ctx != get_context())
+ PRE_INIT_ERROR ("Context not current.\n");
+ if (!evaler->tag_set_eval)
+ PRE_INIT_ERROR ("Calling _eval() with non-tag set parser.\n");
+ Frame prev_ctx_frame = ctx->frame;
+ #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
+ #undef PRE_INIT_ERROR
+
+ mixed conv_result = nil; // Result converted to the expected type.
+ mixed err1 = 0;
+
+ process_tag:
+ do {
+ if ((err1 = catch { // Catch errors but don't allow unwinds.
+ if (array state = ctx->unwind_state && ctx->unwind_state[this]) {
+ #ifdef DEBUG
+ if (in_content)
+ fatal_error ("Can't feed new content when resuming parse.\n");
+ #endif
+ ctx->frame = this;
+ object ignored;
+ [ignored, eval_state, in_args, in_content, iter,
+ subevaler, piece, exec, orig_tag_set, ctx->new_runtime_tags
+ #ifdef DEBUG
+ , debug_iter
+ #endif
+ ] = state;
+ m_delete (ctx->unwind_state, this);
+ if (!sizeof (ctx->unwind_state)) ctx->unwind_state = 0;
+ THIS_TAG_TOP_DEBUG ("Continuing evaluation" +
+ (piece ? " with stream piece\n" : "\n"));
+ }
+
+ else { // Initialize a new evaluation.
+ if (tag) {
+ TRACE_ENTER("tag <" + tag->name + ">", 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");
+ break process_tag;
+ }
+ #endif
+ }
+
+ if (in_content) {
+ THIS_TAG_TOP_DEBUG ("Evaluating\n");
+ ctx->frame = this;
+ if (functionp (args)) {
+ THIS_TAG_DEBUG ("Evaluating compiled arguments\n");
+ in_args = args;
+ args = in_args (ctx);
+ }
content = nil;
}
- }))
+ else if (flags & FLAG_UNPARSED) {
+ #ifdef DEBUG
+ if (args && !mappingp (args))
+ fatal_error ("args is not a mapping in unparsed frame.\n");
+ if (!stringp (content))
+ fatal_error ("content is not a string in unparsed frame.\n");
+ #endif
+ THIS_TAG_TOP_DEBUG ("Evaluating unparsed\n");
+ in_args = _prepare (ctx, type, args);
+ ctx->frame = this;
+ in_content = content;
+ content = nil;
+ flags &= ~FLAG_UNPARSED;
+ }
+ else {
+ _prepare (ctx, type, 0);
+ ctx->frame = this;
+ THIS_TAG_TOP_DEBUG ("Evaluating with constant arguments and content.\n");
+ }
+
+ piece = result = nil;
+ }
+
+ #ifdef DEBUG
+ if (up != prev_ctx_frame)
+ fatal_error ("Frame probably mixed between different simultaneous contexts "
+ "(up: %O, previous ctx->frame: %O).\n", up, prev_ctx_frame);
+ #endif
+
+ })) {
+ #ifdef MODULE_DEBUG
+ if (objectp (err1) && ([object] err1)->thrown_at_unwind)
+ err1 = catch (
+ fatal_error ("Can't save parser state when evaluating arguments.\n"));
+ #endif
break process_tag;
-
+ }
- if (mixed err2 = catch {
+ if (mixed err2 = catch { // Catch errors and allow for unwinds.
+ #ifdef MAGIC_HELP_ARG
+ if (tag && (args || ([]))->help) {
+ TRACE_ENTER ("tag <" + tag->name + " help>", 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);
+ break process_tag;
+ }
+ #endif
+
switch (eval_state) {
case EVSTAT_BEGIN:
if (array|function(RequestID:array) do_enter =
2931:
}
for (; iter > 0; iter-- DO_IF_DEBUG (, debug_iter++)) {
- if (raw_content && raw_content != "")
- if (flags & FLAG_EMPTY_ELEMENT)
+ if (in_content) { // Got nested parsing to do.
+ int finished = 1;
+ if (subevaler)
+ finished = 0; // Continuing an unwound subevaler.
+ else if (stringp (in_content)) {
+ if (in_content == "")
+ subevaler = PCode (content_type);
+ else if (flags & FLAG_EMPTY_ELEMENT)
parse_error ("This tag doesn't handle content.\n");
-
- else { // Got nested parsing to do.
- int finished = 0;
- if (!subparser) { // The nested content is not yet parsed.
+ else { // The nested content is not yet parsed.
if (this->local_tags) {
- subparser = content_type->get_parser (
+ subevaler = content_type->get_pcode_parser (
ctx, [object(TagSet)] this->local_tags, evaler);
- subparser->_local_tag_set = 1;
- THIS_TAG_DEBUG ("Iter[%d]: Evaluating content with %O "
- "from local_tags\n", debug_iter, subparser);
+ subevaler->_local_tag_set = 1;
+ THIS_TAG_DEBUG ("Iter[%d]: Parsing and evaluating content %s "
+ "with %O from local_tags\n", debug_iter,
+ utils->format_short (in_content), subevaler);
}
else {
- subparser = content_type->get_parser (ctx, 0, evaler);
+
#ifdef DEBUG
-
+ if (orig_tag_set)
+ fatal_error ("Didn't expect orig_tag_set here.\n");
+ #endif
+ if (TagSet add_tags = [object(TagSet)] this->additional_tags) {
+ TagSet tset = ctx->tag_set;
+ if (!tset->has_effective_tags (add_tags)) {
+ THIS_TAG_DEBUG ("Installing additional_tags %O\n", add_tags);
+ int hash = HASH_INT2 (tset->id_number, add_tags->id_number);
+ orig_tag_set = tset;
+ TagSet local_ts;
+ if (!(local_ts = local_tag_set_cache[hash])) {
+ local_ts = TagSet (
+ add_tags->name + "+" + orig_tag_set->name);
+ local_ts->imported = ({add_tags, orig_tag_set});
+ // Race, but it doesn't matter.
+ local_tag_set_cache[hash] = local_ts;
+ }
+ ctx->tag_set = local_ts;
+ }
+ else
+ THIS_TAG_DEBUG ("Not installing additional_tags %O "
+ "since they're already in the tag set\n",
+ add_tags);
+ }
+ subevaler = content_type->get_pcode_parser (ctx, 0, evaler);
+ #ifdef DEBUG
if (content_type->parser_prog != PNone)
- THIS_TAG_DEBUG ("Iter[%d]: Evaluating content with %O\n",
- debug_iter, subparser);
+ THIS_TAG_DEBUG ("Iter[%d]: Parsing and evaluating content %s "
+ "with %O\n", debug_iter,
+ utils->format_short (in_content), subevaler);
#endif
}
if (evaler->recover_errors && !(flags & FLAG_DONT_RECOVER))
- subparser->recover_errors = 1;
- subparser->finish (raw_content); // Might unwind.
+ subevaler->recover_errors = 1;
+ subevaler->finish (in_content); // Might unwind.
+ (in_content = subevaler->p_code)->finish();
finished = 1;
}
-
+ }
+ else {
+ THIS_TAG_DEBUG ("Iter[%d]: Evaluating compiled content\n",
+ debug_iter);
+ subevaler = in_content; // Evaling with p-code.
+ }
- process_subparser:
+ eval_sub:
do {
- if (flags & FLAG_STREAM_CONTENT && piece != nil) {
+ 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",
2969:
if (!arrayp (do_process)) {
EXEC_CALLBACK (ctx, evaler, exec, do_process, id, piece);
if (exec) {
- mixed res = _exec_array (ctx, evaler, exec); // Might unwind.
+ mixed res = _exec_array (
+ ctx, evaler, exec, "do_process"); // Might unwind.
if (flags & FLAG_STREAM_RESULT) {
#ifdef DEBUG
if (!zero_type (ctx->unwind_state->stream_piece))
2988:
"ignoring remaining content\n", debug_iter);
ctx->unwind_state = 0;
piece = nil;
- break process_subparser;
+ break eval_sub;
}
}
piece = nil;
-
- if (finished) break process_subparser;
+ if (finished) break eval_sub;
}
- else { // The frame doesn't handle streamed content.
+
+ else { // No streaming.
piece = nil;
if (finished) {
- mixed res = subparser->eval(); // Might unwind.
+ 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 process_subparser;
+ break eval_sub;
}
}
- subparser->finish(); // Might unwind.
+ if (subevaler->is_RXML_Parser) {
+ subevaler->finish(); // Might unwind.
+ (in_content = subevaler->p_code)->finish();
+ }
finished = 1;
- } while (1); // Only loops when an unwound
- // subparser has been recovered.
- subparser = 0;
+ } while (1); // Only loops when an unwound subevaler has been recovered.
+
+ subevaler = 0;
}
if (do_process) {
3030:
if (exec) {
// We don't use EXEC_ARRAY here since it's no idea to
// come back even if any streaming should be done.
- _exec_array (ctx, evaler, exec); // Might unwind.
+ _exec_array (ctx, evaler, exec, "do_return"); // Might unwind.
exec = 0;
}
}
3055:
"for assignment to result\n");
exec = ({content});
}
- _exec_array (ctx, evaler, exec); // Might unwind.
+ _exec_array (ctx, evaler, exec, "content parse"); // Might unwind.
exec = 0;
}
}
3131:
THIS_TAG_TOP_DEBUG ("Interrupted\n");
}
- ustate[this] = ({err2, eval_state, iter, raw_content, subparser, piece,
- exec, orig_tag_set, ctx->new_runtime_tags,
+ ustate[this] = ({err2, eval_state, in_args, in_content, iter,
+ subevaler, piece, exec, orig_tag_set, ctx->new_runtime_tags,
#ifdef DEBUG
debug_iter,
#endif
3156:
"that isn't unwind safe.\n");
#endif
throw (this);
- case "continue": // Continue in this frame through tail recursion.
- return _eval (ctx, evaler, type, 0, 0);
- case "return": // A normal return.
+ case "continue": // Continue in this frame with the stored state.
+ continue process_tag;
+ case "return": // A normal return.
break process_tag;
default:
fatal_error ("Don't you come here and %O on me!\n", action);
3169:
THIS_TAG_TOP_DEBUG ("Done\n");
TRACE_LEAVE ("");
}
- }
+ break process_tag;
+ } while (1); // Looping only when continuing in streaming mode.
// Normal clean up on tag return or exception.
if (orig_tag_set) ctx->tag_set = orig_tag_set;
ctx->frame = up;
ctx->frame_depth--;
if (err1) throw (err1);
-
+ args = in_args, content = in_content;
return conv_result;
}
3593:
// variable names containing ':' are thus not accessible this way.
sscanf (varref, "%[^:]:%s", varref, string encoding);
context->current_var = varref;
+
array(string|int) splitted = context->parse_user_var (varref, 1);
if (splitted[0] == 1)
parse_error (
3603: Inside #if defined(DEBUG)
TAG_DEBUG (context->frame, " Looking up variable %s in context of type %s\n",
splitted * ".", (encoding ? t_string : want_type)->name);
#endif
+
mixed val;
PROFILE_SWITCH (context, "rxml internal", "var:" + varref);
COND_PROF_ENTER(mixed id=context->id,varref,"entity");
3612:
val = nil;
COND_PROF_LEAVE(mixed id=context->id,varref,"entity");
PROFILE_SWITCH (context, "var:" + varref, "rxml internal");
+
if (encoding) {
if (!(val = Roxen->roxen_encode (val + "", encoding)))
parse_error ("Unknown encoding %O.\n", encoding);
3626: Inside #if defined(DEBUG)
if (context->frame)
TAG_DEBUG (context->frame, " Got value %s\n", utils->format_short (val));
#endif
+
context->current_var = 0;
if (evaler->p_code)
evaler->p_code->add (VarRef (splitted[0], splitted[1..], encoding));
return val;
-
+
}) {
context->current_var = 0;
context->handle_exception (err, this_object()); // May throw.
3648:
//! The expected result type of the current stream. (The parser
//! should not do any type checking on this.)
- optional PCode p_code;
+ PCode p_code;
//! Must be set to a new @[PCode] object before a stream is fed
//! which should be compiled to p-code. The object will receive the
//! compiled code during evaluation and can be used to repeat the
- //! evaluation after the stream is finished. Parsers that doesn't
- //! support compilation doesn't have this defined.
+ //! evaluation after the stream is finished.
//! @decl int unwind_safe;
//!
3737:
//(!) Internals:
+ mixed _eval (Context ignored) {return eval();}
+ // To be call compatible with PCode.
+
Parser _next_free;
// Used to link together unused parser objects for reuse.
Parser _parent;
- // The parent parser if this one is nested.
+ // The parent parser if this one is nested. This is only used to
+ // register runtime tags.
Stdio.File _source_file;
mapping _defines;
3988:
return newtype;
}
- inline Parser get_parser (Context ctx, void|TagSet tag_set, void|Parser|PCode parent)
+ inline final Parser get_parser (Context ctx, void|TagSet tag_set,
+ void|Parser|PCode parent)
//! Returns a parser instance initialized with the given context.
{
Parser p;
3997:
if (parent && parent->is_RXML_TagSetParser &&
tset == parent->tag_set && sizeof (ctx->runtime_tags) &&
- parent->clone && parent->type == this_object()) {
+ parent->clone && parent->type->name == this_object()->name) {
// There are runtime tags. Try to clone the parent parser if
// all conditions are met.
p = parent->clone (ctx, this_object(), tset, @parser_args);
4084:
return p;
}
- mixed eval (string in, void|Context ctx, void|TagSet tag_set,
- void|Parser|PCode parent, void|int dont_switch_ctx)
- //! Parses and evaluates the value in the given string. If a context
- //! isn't given, the current one is used. The current context and
- //! ctx are assumed to be the same if dont_switch_ctx is nonzero.
+ Parser get_pcode_parser (Context ctx, void|TagSet tag_set, void|Parser|PCode parent)
+ //! Like @[get_parser], but also initializes a PCode object in the
+ //! returned parser.
{
- mixed res;
- if (!ctx) ctx = get_context();
- if (parser_prog == PNone) res = in;
- else {
+
Parser p = get_parser (ctx, tag_set, parent);
- p->_parent = parent;
- if (dont_switch_ctx) p->finish (in); // Optimize the job in p->write_end().
- else p->write_end (in);
- res = p->eval();
- if (p->reset) {
- p->context = p->recover_errors = p->_parent = 0;
+ p->p_code = PCode (this_object());
+ return p;
+ }
+
+ inline final void give_back (Parser parser, void|TagSet tag_set)
+ //! Returns the given parser object for reuse. Only has effect if
+ //! the parser implements @[Parser.reset]. If the parser is a tag
+ //! set parser, tag_set must specify the tag set it uses.
+ {
+ #ifdef DEBUG
+ if (parser->type->name != this_object()->name)
+ error ("Giving back parser to wrong type.\n");
+ #endif
+ if (parser->reset) {
+ parser->context = parser->recover_errors = parser->_parent = 0;
#ifdef RXML_OBJ_DEBUG
- p->__object_marker->create (p);
+ parser->__object_marker->create (p);
#endif
if (_p_cache) {
- if (PCacheObj pco = _p_cache[tag_set || ctx->tag_set]) {
+ if (PCacheObj pco = _p_cache[tag_set]) {
// Relying on interpreter lock here.
- p->_next_free = pco->free_parser;
- pco->free_parser = p;
+ parser->_next_free = pco->free_parser;
+ pco->free_parser = parser;
}
}
else {
// Relying on interpreter lock in this block.
- p->_next_free = free_parser;
- free_parser = p;
+ parser->_next_free = free_parser;
+ free_parser = parser;
}
}
}
-
+
+ mixed eval (string in, void|Context ctx, void|TagSet tag_set,
+ void|Parser|PCode parent, void|int dont_switch_ctx)
+ //! Parses and evaluates the value in the given string. If a context
+ //! isn't given, the current one is used. The current context and
+ //! ctx are assumed to be the same if dont_switch_ctx is nonzero.
+ {
+ mixed res;
+ if (!ctx) ctx = get_context();
+ if (parser_prog == PNone) res = in;
+ else {
+ if (!tag_set) tag_set = ctx->tag_set;
+ Parser parser = get_parser (ctx, tag_set, parent);
+ parser->_parent = parent;
+ if (dont_switch_ctx) parser->finish (in); // Optimize the job in write_end().
+ else parser->write_end (in);
+ res = parser->eval();
+ give_back (parser, tag_set);
+ }
if (ctx->type_check) type_check (res);
return res;
}
4540:
TScalar t_scalar = TScalar();
//! Any type of scalar, i.e. text or number. It's not sequential, as
//! opposed to the subtype @[RXML.t_string].
- //!
- //! FIXME: This is currently not labeled as a supertype for t_string
- //! and t_num, so it's only marginally useful. It's name will probably
- //! change.
+
static class TScalar
{
4580:
TNum t_num = TNum();
//! Type for any number, currently integer or float.
- //!
- //! FIXME: This is currently not labeled as a supertype for t_int and
- //! t_float, so it's only marginally useful. It's name will probably
- //! change.
+
static class TNum
{
4930:
mixed get (Context ctx, void|Type want_type)
{
+ #ifdef DEBUG
+ if (ctx->frame)
+ TAG_DEBUG (ctx->frame, " Looking up variable %s.%s in context of type %s\n",
+ scope, arrayp (var) ? var * "." : var,
+ (encoding ? t_string : want_type)->name);
+ #endif
+
if (encoding) {
string val = ctx->get_var (var, scope, t_string);
if (!(val = Roxen->roxen_encode (val + "", encoding)))
parse_error ("Unknown encoding %O.\n", encoding);
-
+ #ifdef DEBUG
+ if (ctx->frame)
+ TAG_DEBUG (ctx->frame, " Got value %s after conversion "
+ "with encoding %s\n", utils->format_short (val), encoding);
+ #endif
return val;
}
- else return ctx->get_var (var, scope, want_type);
+
+ else {
+ mixed val;
+ if (zero_type (val = ctx->get_var (var, scope, want_type))) val = nil;
+ #ifdef DEBUG
+ if (ctx->frame)
+ TAG_DEBUG (ctx->frame, " Got value %s\n", utils->format_short (val));
+ return val;
+ #else
+ return ctx->get_var (var, scope, want_type);
+ #endif
}
-
+ }
mixed set (Context ctx, mixed val) {return ctx->set_var (var, val, scope);}
4949:
string _sprintf() {return "RXML.VarRef(" + name() + ")" + OBJ_COUNT;}
}
+ class PikeCompile
+ //! Helper class to paste together a Pike program from strings.
+ {
+ private int idnr = 0;
+ /*private*/ String.Buffer code = String.Buffer();
+ /*private*/ mapping(string:mixed) bindings = ([]);
+
+ string bind (mixed val)
+ {
+ string id = "b" + idnr++;
+ bindings[id] = val;
+ return id;
+ }
+
+ string decl_const (string init)
+ {
+ string id = "c" + idnr++;
+ code->add (sprintf ("constant %s = %s;\n", id, init));
+ return id;
+ }
+
+ string decl_var (string type, void|string init)
+ {
+ string id = "v" + idnr++;
+ if (init)
+ code->add (sprintf ("%s %s = %s;\n", type, id, init));
+ else
+ code->add (sprintf ("%s %s;\n", type, init));
+ return id;
+ }
+
+ string decl_func (string rettype, string arglist, string def)
+ {
+ string id = "f" + idnr++;
+ code->add (sprintf ("%s %s (%s)\n{%s}\n\n", rettype, id, arglist, def));
+ return id;
+ }
+
+ program compile()
+ {
+ program res;
+ string txt = code->get();
+ #ifdef DEBUG
+ if (mixed err = catch {
+ #endif
+ res = compile_string (
+ txt, 0,
+ class (object master) {
+ void compile_error (string file, int line, string err)
+ {master->compile_error (file, line, err);}
+ void compile_warning (string file, int line, string err)
+ {master->compile_warning (file, line, err);}
+ mixed resolv (string id, void|string file)
+ {
+ mixed val;
+ if (!zero_type (val = bindings[id])) return val;
+ return master->resolv (id, file);
+ }
+ } (master()));
+ #ifdef DEBUG
+ }) {
+ code = 0;
+ werror ("Failed program: %s\n", txt);
+ throw (err);
+ }
+ code = 0;
+ #endif
+ return res;
+ }
+ }
+
class PCode
//! Holds p-code and evaluates it. P-code is the intermediate form
//! after parsing and before evaluation.
{
constant is_RXML_PCode = 1;
constant thrown_at_unwind = 1;
-
+ constant tag_set_eval = 1;
Type type;
//! The type the p-code evaluates to. Should be the same as the
4971:
mixed eval (Context context)
//! Evaluates the p-code in the given context.
{
- mixed res = nil;
- int pos = 0;
-
+ mixed res;
ENTER_CONTEXT (context);
if (mixed err = catch {
if (context && context->unwind_state && context->unwind_state->top) {
#ifdef MODULE_DEBUG
if (context->unwind_state->top != this_object())
- fatal_error ("The context got an unwound state from another PCode object. "
- "Can't rewind.\n");
+ fatal_error ("The context got an unwound state "
+ "from another evaluator object. Can't continue.\n");
#endif
- pos = m_delete (context->unwind_state, "pos");
+
m_delete (context->unwind_state, "top");
if (!sizeof (context->unwind_state)) context->unwind_state = 0;
}
-
- // Note: Remember to initialize Frame.content and Frame.result
- // when reusing frames.
-
+ res = _eval (context); // Might unwind.
})
if (objectp (err) && ([object] err)->thrown_at_unwind) {
- #ifdef DEBUG
- if (err != this_object()) {
- LEAVE_CONTEXT();
- fatal_error ("Unexpected unwind object catched.\n");
- }
- #endif
+
if (!context->unwind_state) context->unwind_state = ([]);
- context->unwind_state->p_code_pos = pos;
- context->unwind_state->top = err;
+ context->unwind_state->top = this_object();
}
else {
LEAVE_CONTEXT();
throw_fatal (err);
}
-
+
LEAVE_CONTEXT();
return res;
}
- function(Context:mixed) compile();
- //! Returns a compiled function for doing the evaluation. The
- //! function will receive a context to do the evaluation in.
+ //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)
{
5025:
static array p_code = allocate (16);
static int length = 0;
+ static string errmsgs;
void add (mixed entry)
{
5034:
void finish()
{
+ if (length != sizeof (p_code))
p_code = p_code[..length - 1];
}
- void report_error (string msg)
+ mixed _eval (Context context)
+ //! Like @[eval], but assumes the given context is current. Mostly
+ //! for internal use.
{
- // FIXME
+ int pos = 0;
+ array parts;
+ int ppos = 0;
+
+ if (mixed err = catch {
+ if (context && context->unwind_state) {
+ object ignored;
+ [ignored, pos, parts, ppos] = m_delete (context->unwind_state, this_object());
}
-
+ else parts = allocate (length);
-
+ for (; pos < length; pos++) {
+ mixed item = p_code[pos];
+ if (objectp (item))
+ if (item->is_RXML_Frame) {
+ item = item->_eval (
+ context, this_object(), type, item->content); // Might unwind.
+ if (errmsgs) item += errmsgs, errmsgs = 0;
+ }
+ else if (item->is_RXML_VarRef) {
+ item = item->get (context, type); // Might unwind.
+ if (errmsgs) item += errmsgs, errmsgs = 0;
+ }
+ if (item != nil)
+ parts[ppos++] = item;
+ }
+
+ if (!ppos)
+ return type->sequential ? type->empty_value : nil;
+ else
+ if (type->sequential)
+ return `+ (type->empty_value, @parts[..ppos - 1]);
+ else
+ if (ppos != 1) return utils->get_non_nil (type, @parts[..ppos - 1]);
+ else return parts[0];
+
+ })
+ if (objectp (err) && ([object] err)->thrown_at_unwind) {
+ context->unwind_state[this_object()] = ({err, pos, parts, ppos});
+ throw (this_object());
+ }
+ else throw_fatal (err);
+ }
+
+ string _compile_text (PikeCompile comp)
+ //! Returns a string containing a Pike expression that evaluates the
+ //! value of this @[PCode] object, assuming the current context is
+ //! in a variable named @tt{context@}. No code to handle exception
+ //! unwinding and rewinding is added. Mostly for internal use.
+ {
+ if (!length)
+ return type->sequential ? comp->bind (type->empty_value) : "RXML.nil";
+
+ string typevar = comp->bind (type);
+ array(string) parts = allocate (length);
+
+ for (int pos = 0; pos < length; pos++) {
+ mixed item = p_code[pos];
+ if (objectp (item))
+ if (item->is_RXML_Frame) {
+ string itemvar = comp->bind (p_code[pos]);
+ parts[pos] = sprintf ("%s->_eval (context, 0, %s, %s->content)",
+ itemvar, typevar, itemvar);
+ continue;
+ }
+ else if (item->is_RXML_VarRef) {
+ parts[pos] = sprintf ("%s->get (context, %s)",
+ comp->bind (p_code[pos]), typevar);
+ continue;
+ }
+ parts[pos] = comp->bind (p_code[pos]);
+ }
+
+ if (type->sequential)
+ return comp->bind (type->empty_value) + " + " + parts * " + ";
+ else
+ if (length == 1) return parts[0];
+ else return sprintf ("RXML.utils.get_non_nil (%s, %s)", typevar, parts * ", ");
+ }
+
+ int report_error (string msg)
+ {
+ if (errmsgs) errmsgs += msg;
+ else errmsgs = msg;
+ return 1;
+ }
+
MARK_OBJECT;
string _sprintf() {return "RXML.PCode" + OBJ_COUNT;}
5218:
//(!) Various internal kludges:
+ static Type splice_arg_type;
+
static object/*(Parser.HTML)*/
- charref_decode_parser, lowercaser, uppercaser, capitalizer;
+ xml_splice_arg_parser, charref_decode_parser, lowercaser, uppercaser, capitalizer;
static void init_parsers()
{
- // Pretty similar to PEnt..
+
object/*(Parser.HTML)*/ p = Parser_HTML();
-
+ p->match_tag (0);
+ xml_splice_arg_parser = p;
+
+ // Pretty similar to PEnt..
+ p = Parser_HTML();
p->lazy_entity_end (1);
p->add_entities (Roxen->parser_charref_table);
p->_set_entity_callback (
5313:
mixed err = catch {
switch (name) {
case "PXml": PXml = [program] val; break;
- case "PEnt": PEnt = [program] val; break;
+ case "PEnt":
+ PEnt = [program] val;
+ splice_arg_type = t_string (PEnt);
+ break;
case "PExpr": PExpr = [program] val; break;
case "utils": utils = [object] val; break;
case "Roxen": Roxen = [object] val; init_parsers(); break;