|
|
|
|
|
|
|
|
static object Roxen; |
static object roxen; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h> |
#include <module.h> |
#include <request_trace.h> |
|
#define MAGIC_HELP_ARG |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef RXML_OBJ_DEBUG |
# define MARK_OBJECT \ |
mapping|object __object_marker = RoxenDebug.ObjectMarker (this_object()) |
# define MARK_OBJECT_ONLY \ |
mapping|object __object_marker = RoxenDebug.ObjectMarker (0) |
#else |
# define MARK_OBJECT ; |
# define MARK_OBJECT_ONLY ; |
#endif |
|
#ifdef OBJ_COUNT_DEBUG |
|
|
# ifndef RXML_OBJ_DEBUG |
# undef MARK_OBJECT |
# undef MARK_OBJECT_ONLY |
# define MARK_OBJECT \ |
mapping|object __object_marker = (["count": ++all_constants()->_obj_count]) |
# define MARK_OBJECT_ONLY \ |
mapping|object __object_marker = (["count": ++all_constants()->_obj_count]) |
# endif |
# define OBJ_COUNT (__object_marker ? "[" + __object_marker->count + "]" : "") |
#else |
# define OBJ_COUNT "" |
#endif |
|
#ifdef DEBUG |
# define TAG_DEBUG(frame, msg, args...) \ |
(TAG_DEBUG_TEST (frame && frame->flags & FLAG_DEBUG) && \ |
report_debug ("%O: " + (msg), (frame), args), 0) |
#else |
# define TAG_DEBUG(frame, msg, args...) 0 |
#endif |
|
#ifdef FRAME_DEPTH_DEBUG |
# define FRAME_DEPTH_MSG(msg...) report_debug (msg) |
#else |
# define FRAME_DEPTH_MSG(msg...) |
#endif |
|
#ifdef RXML_PCODE_UPDATE_DEBUG |
# define PCODE_UPDATE_MSG(msg...) report_debug (msg) |
#else |
# define PCODE_UPDATE_MSG(msg...) |
#endif |
|
#define _LITERAL(X) #X |
#define LITERAL(X) _LITERAL (X) |
|
#define HASH_INT2(m, n) (n < 65536 ? (m << 16) + n : sprintf ("%x,%x", m, n)) |
|
#undef RXML_CONTEXT |
#define RXML_CONTEXT (_cur_rxml_context->get()) |
#define SET_RXML_CONTEXT(ctx) (_cur_rxml_context->set (ctx)) |
|
|
#define SCOPE_TYPE mapping(string:mixed)|object(Scope) |
#define UNWIND_STATE mapping(string|object:mixed|array) |
#define EVAL_ARGS_FUNC function(Context,Parser|PCode:mapping(string:mixed))|string |
|
|
|
|
|
|
static int tag_set_count = 0; |
|
static mapping(int|string:TagSet) garb_composite_tag_set_cache() |
{ |
call_out (garb_composite_tag_set_cache, 30*60); |
return composite_tag_set_cache = ([]); |
} |
|
static mapping(int|string:TagSet) composite_tag_set_cache = |
garb_composite_tag_set_cache(); |
|
#define GET_COMPOSITE_TAG_SET(a, b, res) do { \ |
int|string hash = HASH_INT2 (b->id_number, a->id_number); \ |
if (!(res = composite_tag_set_cache[hash])) { \ |
res = TagSet (0, 0); \ |
res->imported = ({a, b}); \ |
/* Race, but it doesn't matter. */ \ |
composite_tag_set_cache[hash] = res; \ |
} \ |
} while (0) |
|
static mapping(RoxenModule|Configuration:mapping(string:TagSet|int)) all_tag_sets = ([]); |
|
|
|
|
|
|
|
|
|
|
static Thread.Mutex all_tag_sets_mutex = Thread.Mutex(); |
|
#define LOOKUP_TAG_SET(owner, name) ((all_tag_sets[owner] || ([]))[name]) |
|
|
#define SET_TAG_SET(owner, name, value) do { \ |
mapping(string:TagSet|int) map = \ |
all_tag_sets[owner] || \ |
(all_tag_sets[owner] = set_weak_flag (([]), 1)); \ |
map[name] = (value); \ |
} while (0) |
|
static mapping(string:program) reg_parsers = ([]); |
|
|
static mapping(string:Type) reg_types = ([]); |
|
|
static mapping(mixed:string) reverse_constants = set_weak_flag (([]), 1); |
|
|
|
|
class Tag |
|
{ |
constant is_RXML_Tag = 1; |
constant is_RXML_encodable = 1; |
constant is_RXML_p_code_frame = 1; |
constant is_RXML_p_code_entry = 1; |
|
|
|
|
|
|
|
TagSet tagset; |
|
|
|
|
|
|
|
mapping(string:Type) req_arg_types = ([]); |
mapping(string:Type) opt_arg_types = ([]); |
|
|
|
|
Type def_arg_type = t_text (PEnt); |
|
|
|
|
Type content_type = t_same (PXml); |
|
|
|
|
|
|
|
|
|
|
|
|
|
array(Type) result_types = ({t_string, t_any_text}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final object `() (mapping(string:mixed) args, void|mixed content) |
|
|
|
{ |
object frame = |
([function(:object)] this_object()->Frame)(); |
frame->tag = this_object(); |
frame->flags = this_object()->flags; |
frame->args = args; |
frame->content = zero_type (content) ? nil : content; |
return frame; |
} |
|
int eval_args (mapping(string:mixed) args, void|int dont_throw, |
void|Context ctx, void|array(string) ignore_args) |
|
|
|
|
|
|
|
|
|
{ |
|
|
mapping(string:Type) atypes = args & req_arg_types; |
if (sizeof (atypes) < sizeof (req_arg_types)) |
if (dont_throw) return 0; |
else { |
array(string) missing = sort (indices (req_arg_types - atypes)); |
parse_error ("Required " + |
(sizeof (missing) > 1 ? |
"arguments " + String.implode_nicely (missing) + " are" : |
"argument " + missing[0] + " is") + " missing.\n"); |
} |
atypes += args & opt_arg_types; |
#ifdef MODULE_DEBUG |
if (mixed err = catch { |
#endif |
foreach (indices (args) - ( ignore_args||({}) ), string arg) |
args[arg] = (atypes[arg] || def_arg_type)->eval ( |
args[arg], ctx); |
#ifdef MODULE_DEBUG |
}) { |
if (objectp (err) && ([object] err)->thrown_at_unwind) |
fatal_error ("Can't save parser state when evaluating arguments.\n"); |
throw_fatal (err); |
} |
#endif |
return 1; |
} |
|
|
|
#define MAKE_FRAME(_frame, _ctx, _parser, _args, _content) \ |
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; \ |
break make_new_frame; \ |
} \ |
_frame = \ |
([function(:object/*(Frame)HMM*/)] this_object()->Frame)(); \ |
_frame->tag = this_object(); \ |
_frame->flags = this_object()->flags|FLAG_UNPARSED; \ |
_frame->args = _args; \ |
_frame->content = _content || ""; \ |
DO_IF_DEBUG( \ |
if (_args && ([mapping] (mixed) _args)["_debug_"]) { \ |
_frame->flags |= FLAG_DEBUG; \ |
m_delete (_args, "_debug_"); \ |
} \ |
); \ |
} while (0) |
|
#define EVAL_FRAME(_frame, _ctx, _parser, _type, _res) \ |
do { \ |
EVAL_ARGS_FUNC argfunc = 0; \ |
int orig_state_updated = _ctx->state_updated; \ |
if (mixed err = catch { \ |
_res = _frame->_eval (_ctx, _parser, _type); \ |
if (PCode p_code = _parser->p_code) \ |
p_code->add_frame (_ctx, _frame, _res, 1); \ |
}) \ |
if (objectp (err) && ([object] err)->thrown_at_unwind) { \ |
UNWIND_STATE ustate = _ctx->unwind_state; \ |
if (!ustate) ustate = _ctx->unwind_state = ([]); \ |
DO_IF_DEBUG ( \ |
if (err != _frame) \ |
fatal_error ("Unexpected unwind object catched.\n"); \ |
if (ustate[_parser]) \ |
fatal_error ("Clobbering unwind state for parser.\n"); \ |
); \ |
ustate[_parser] = ({_frame}); \ |
throw (_parser); \ |
} \ |
else { \ |
if (PCode p_code = _parser->p_code) { \ |
PCODE_UPDATE_MSG ( \ |
"%O: Restoring p-code update count from %d to %d " \ |
"since the frame is stored unevaluated " \ |
"due to exception.\n", \ |
_frame, _ctx->state_updated, orig_state_updated); \ |
_ctx->state_updated = orig_state_updated; \ |
p_code->add_frame (_ctx, _frame, PCode, 1); \ |
} \ |
ctx->handle_exception ( \ |
err, _parser); /* Will rethrow unknown errors. */ \ |
_res = nil; \ |
} \ |
} while (0) |
|
final mixed handle_tag (TagSetParser parser, mapping(string:string) args, |
void|string content) |
|
|
{ |
|
Context ctx = parser->context; |
object frame; |
MAKE_FRAME (frame, ctx, parser, args, content); |
if (!zero_type (frame->raw_tag_text)) |
frame->raw_tag_text = parser->raw_tag_text(); |
mixed result; |
EVAL_FRAME (frame, ctx, parser, parser->type, result); |
return result; |
} |
|
final array _p_xml_handle_tag (object parser, mapping(string:string) args, |
void|string content) |
{ |
Type type = parser->type; |
parser->drain_output(); |
Context ctx = parser->context; |
object frame; |
MAKE_FRAME (frame, ctx, parser, args, content); |
if (!zero_type (frame->raw_tag_text)) |
frame->raw_tag_text = parser->current_input(); |
mixed result; |
EVAL_FRAME (frame, ctx, parser, type, result); |
if (result != nil) parser->add_value (result); |
return ({}); |
} |
|
final array _p_xml_handle_pi_tag (object parser, string content) |
{ |
Type type = parser->type; |
parser->drain_output(); |
|
sscanf (content, "%[ \t\n\r]%s", string ws, string rest); |
if (ws == "" && rest != "") { |
|
|
if (!type->free_text) |
return utils->unknown_pi_tag_error (parser, content); |
return 0; |
} |
|
Context ctx = parser->context; |
object frame; |
MAKE_FRAME (frame, ctx, parser, 0, content); |
if (!zero_type (frame->raw_tag_text)) |
frame->raw_tag_text = parser->current_input(); |
mixed result; |
EVAL_FRAME (frame, ctx, parser, type, result); |
if (result != nil) parser->add_value (result); |
return ({}); |
} |
|
mapping(string:mixed) _eval_splice_args (Context ctx, |
mapping(string:string) raw_args, |
mapping(string:Type) my_req_args) |
|
|
{ |
|
mapping(string:Type) atypes = |
raw_args & (req_arg_types | opt_arg_types); |
if (my_req_args) { |
mapping(string:Type) missing = my_req_args - atypes; |
if (sizeof (missing)) |
parse_error ("Required " + |
(sizeof (missing) > 1 ? |
"arguments " + String.implode_nicely ( |
sort (indices (missing))) + " are" : |
"argument " + indices (missing)[0] + " is") + " missing.\n"); |
} |
|
#ifdef MODULE_DEBUG |
if (mixed err = catch { |
#endif |
foreach (indices (raw_args), string arg) { |
Type t = atypes[arg] || def_arg_type; |
if (t->parser_prog != PNone) { |
Parser parser = t->get_parser (ctx, ctx->tag_set, 0); |
TAG_DEBUG (RXML_CONTEXT->frame, |
"Evaluating argument value %s with %O\n", |
format_short (raw_args[arg]), parser); |
parser->finish (raw_args[arg]); |
raw_args[arg] = parser->eval(); |
TAG_DEBUG (RXML_CONTEXT->frame, |
"Setting dynamic argument %s to %s\n", |
format_short (arg), |
format_short (raw_args[arg])); |
t->give_back (parser, ctx->tag_set); |
} |
} |
#ifdef MODULE_DEBUG |
}) { |
if (objectp (err) && ([object] err)->thrown_at_unwind) |
fatal_error ("Can't save parser state when evaluating dynamic arguments.\n"); |
throw_fatal (err); |
} |
#endif |
|
return raw_args; |
} |
|
|
MARK_OBJECT; |
|
|
string _sprintf() |
{ |
return "RXML.Tag(" + [string] this_object()->name + |
(this_object()->plugin_name ? "#" + [string] this_object()->plugin_name : "") + |
([int] this_object()->flags & FLAG_PROC_INSTR ? " [PI]" : "") + ")" + |
OBJ_COUNT; |
} |
} |
|
|
class TagSet |
|
|
|
|
|
|
|
|
|
|
|
{ |
constant is_RXML_TagSet = 1; |
|
RoxenModule|Configuration owner; |
|
|
|
|
|
string name; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(TagSet) imported = ({}); |
|
|
|
|
|
|
|
|
|
|
|
function(Context:void) prepare_context; |
|
|
|
|
|
|
function(Context:void) eval_finish; |
|
|
|
|
int generation = 1; |
|
|
|
int id_number; |
|
|
static void create (RoxenModule|Configuration owner_, string name_, |
void|array(Tag) _tags) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
id_number = ++tag_set_count; |
if (name_) { |
Thread.MutexKey key = all_tag_sets_mutex->lock (2); |
|
|
set_name (owner_, name_); |
key = 0; |
} |
else owner = owner_; |
if (_tags) add_tags (_tags); |
#ifdef RXML_OBJ_DEBUG |
__object_marker->create (this_object()); |
#endif |
} |
|
void add_tag (Tag tag) |
|
{ |
#ifdef MODULE_DEBUG |
if (!stringp (tag->name)) |
error ("Trying to register a tag %O without a name.\n", tag); |
if (!callablep (tag->Frame) && !tag->plugin_name) |
error ("Trying to register a tag %O without a Frame class or function.\n", tag); |
if (tag->name[..3] != "!--#" && |
replace (tag->name, "#<>& \t\n\r" / "", ({""}) * 8) != tag->name) |
error ("Invalid character(s) in name for tag %O.\n", tag); |
#endif |
if (tag->flags & FLAG_PROC_INSTR) { |
if (!proc_instrs) proc_instrs = ([]); |
if (tag->plugin_name) proc_instrs[tag->name + "#" + tag->plugin_name] = tag; |
else proc_instrs[tag->name] = tag; |
} |
else |
if (tag->plugin_name) tags[tag->name + "#" + tag->plugin_name] = tag; |
else tags[tag->name] = tag; |
changed(); |
} |
|
void add_tags (array(Tag) _tags) |
|
{ |
foreach (_tags, Tag tag) { |
#ifdef MODULE_DEBUG |
if (!stringp (tag->name)) |
error ("Trying to register a tag %O without a name.\n", tag); |
if (!callablep (tag->Frame)&& !tag->plugin_name) |
error ("Trying to register a tag %O without a Frame class or function.\n", tag); |
if (tag->name[..3] != "!--#" && |
replace (tag->name, "#<>& \t\n\r" / "", ({""}) * 8) != tag->name) |
error ("Invalid character(s) in name for tag %O.\n", tag); |
#endif |
if (tag->flags & FLAG_PROC_INSTR) { |
if (!proc_instrs) proc_instrs = ([]); |
if (tag->plugin_name) proc_instrs[tag->name + "#" + tag->plugin_name] = tag; |
else proc_instrs[tag->name] = tag; |
} |
else |
if (tag->plugin_name) tags[tag->name + "#" + tag->plugin_name] = tag; |
else tags[tag->name] = tag; |
tag->tagset = this_object(); |
} |
changed(); |
} |
|
void remove_tag (string|Tag tag, void|int proc_instr) |
|
|
|
|
{ |
if (stringp (tag)) |
if (proc_instr) { |
if (proc_instrs) m_delete (proc_instrs, tag); |
} |
else |
m_delete (tags, tag); |
else { |
string n; |
if (tag->flags & FLAG_PROC_INSTR) { |
if (proc_instrs && !zero_type (n = search (tags, [object(Tag)] tag))) |
m_delete (proc_instrs, n); |
} |
else |
if (!zero_type (n = search (tags, [object(Tag)] tag))) |
m_delete (tags, n); |
} |
changed(); |
} |
|
void clear() |
|
|
{ |
tags = ([]), proc_instrs = 0; |
clear_string_entities(); |
} |
|
local Tag get_local_tag (string name, void|int proc_instr) |
|
|
|
{ |
return proc_instr ? proc_instrs && proc_instrs[name] : tags[name]; |
} |
|
local array(Tag) get_local_tags() |
|
{ |
array(Tag) res = values (tags); |
if (proc_instrs) res += values (proc_instrs); |
return res; |
} |
|
local Tag get_tag (string name, void|int proc_instr) |
|
|
|
|
{ |
if (object(Tag) def = get_local_tag (name, proc_instr)) |
return def; |
foreach (imported, TagSet tag_set) |
if (object(Tag) tag = [object(Tag)] tag_set->get_tag (name, proc_instr)) |
return tag; |
return 0; |
} |
|
local int has_tag (Tag tag) |
|
|
{ |
return !!get_tag (tag->name, tag->flags & FLAG_PROC_INSTR); |
} |
|
local multiset(string) get_tag_names() |
|
{ |
return `| ((multiset) indices (tags), @imported->get_tag_names()); |
} |
|
local multiset(string) get_proc_instr_names() |
|
{ |
return `| (proc_instrs ? (multiset) indices (proc_instrs) : (<>), |
@imported->get_proc_instr_names()); |
} |
|
local Tag get_overridden_tag (Tag overrider) |
|
|
{ |
if (!mappingp (overridden_tag_lookup)) |
overridden_tag_lookup = set_weak_flag (([]), 1); |
Tag tag; |
if (zero_type (tag = overridden_tag_lookup[overrider])) { |
string overrider_name = overrider->plugin_name ? |
overrider->plugin_name + "#" + overrider->name : overrider->name; |
tag = overridden_tag_lookup[overrider] = |
overrider->flags & FLAG_PROC_INSTR ? |
find_overridden_proc_instr (overrider, overrider_name) : |
find_overridden_tag (overrider, overrider_name); |
} |
return tag; |
} |
|
local array(Tag) get_overridden_tags (string name, void|int proc_instr) |
|
|
|
|
{ |
if (object(Tag) def = get_local_tag (name, proc_instr)) |
return ({def}) + imported->get_overridden_tags (name, proc_instr) * ({}); |
else |
return imported->get_overridden_tags (name, proc_instr) * ({}); |
} |
|
void add_string_entities (mapping(string:string) entities) |
|
|
|
{ |
if (string_entities) string_entities |= entities; |
else string_entities = entities + ([]); |
changed(); |
} |
|
void clear_string_entities() |
|
{ |
string_entities = 0; |
changed(); |
} |
|
local mapping(string:string) get_string_entities() |
|
|
{ |
if (string_entities) |
return `+(@imported->get_string_entities(), string_entities); |
else |
return `+(@imported->get_string_entities(), ([])); |
} |
|
local mapping(string:Tag) get_plugins (string name, void|int proc_instr) |
|
|
|
|
{ |
mapping(string:Tag) res; |
if (proc_instr) { |
if (!pi_plugins) pi_plugins = ([]); |
if ((res = pi_plugins[name])) return res; |
low_get_pi_plugins (name + "#", res = ([])); |
return pi_plugins[name] = res; |
} |
else { |
if (!plugins) plugins = ([]); |
if ((res = plugins[name])) return res; |
low_get_plugins (name + "#", res = ([])); |
return plugins[name] = res; |
} |
} |
|
local string get_hash() |
|
|
|
|
|
|
|
|
|
{ |
if (!hash) |
hash = Crypto.md5()->update (encode_value_canonic (get_hash_data()))->digest(); |
return hash; |
} |
|
local void add_tag_set_dependency (TagSet tset) |
|
|
|
|
|
|
|
|
{ |
dep_tag_sets[tset] = 1; |
tset->do_notify (changed); |
changed(); |
} |
|
local void remove_tag_set_dependency (TagSet tset) |
|
{ |
dep_tag_sets[tset] = 0; |
tset->dont_notify (changed); |
} |
|
local int has_effective_tags (TagSet tset) |
|
{ |
return tset == top_tag_set && !got_local_tags; |
} |
|
local mixed `->= (string var, mixed val) |
{ |
switch (var) { |
case "owner": { |
Thread.MutexKey key = all_tag_sets_mutex->lock (2); |
|
|
if (name) SET_TAG_SET (owner, name, generation); |
set_name (val, name); |
break; |
} |
case "name": { |
Thread.MutexKey key = all_tag_sets_mutex->lock (2); |
|
|
if (name) SET_TAG_SET (owner, name, generation); |
set_name (owner, val); |
break; |
} |
case "imported": |
if (!val) return val; |
filter (imported, "dont_notify", changed); |
imported = [array(TagSet)] val; |
imported->do_notify (changed); |
top_tag_set = sizeof (imported) && imported[0]; |
break; |
default: |
::`->= (var, val); |
} |
changed(); |
return val; |
} |
|
local mixed `[]= (string var, mixed val) {return `->= (var, val);} |
|
final Context new_context (void|RequestID id) |
|
|
|
|
{ |
Context ctx = Context (this_object(), id); |
call_prepare_funs (ctx); |
return ctx; |
} |
|
final Parser get_parser (Type top_level_type, void|RequestID id, void|int make_p_code) |
|
|
|
|
{ |
return new_context (id)->new_parser (top_level_type, make_p_code); |
} |
|
final Parser `() (Type top_level_type, void|RequestID id, void|int make_p_code) |
|
{ |
return get_parser (top_level_type, id, make_p_code); |
} |
|
void changed() |
|
|
{ |
#ifdef TAGSET_GENERATION_DEBUG |
werror ("%O update, generation %d -> %d\n", this_object(), |
generation, generation + 1); |
#endif |
generation++; |
prepare_funs = 0; |
overridden_tag_lookup = 0; |
plugins = pi_plugins = 0; |
hash = 0; |
(notify_funcs -= ({0}))(); |
set_weak_flag (notify_funcs, 1); |
got_local_tags = sizeof (tags) || (proc_instrs && sizeof (proc_instrs)); |
#ifdef TAGSET_GENERATION_DEBUG |
werror ("%O update done, generation %d -> %d\n", this_object(), |
generation - 1, generation); |
#endif |
} |
|
function(Backtrace,Type:string) handle_run_error = |
lambda (Backtrace err, Type type) |
|
|
|
{ |
string result; |
foreach(imported, TagSet tag_set) { |
result = tag_set->handle_run_error(err, type); |
if(result) return result; |
} |
return 0; |
}; |
|
function(Backtrace,Type:string) handle_parse_error = |
lambda (Backtrace err, Type type) |
|
|
|
{ |
string result; |
foreach(imported, TagSet tag_set) { |
result = tag_set->handle_parse_error(err, type); |
if(result) return result; |
} |
return 0; |
}; |
|
|
|
void do_notify (function(:void) func) |
{ |
notify_funcs |= ({func}); |
set_weak_flag (notify_funcs, 1); |
} |
|
void dont_notify (function(:void) func) |
{ |
notify_funcs -= ({func}); |
set_weak_flag (notify_funcs, 1); |
} |
|
array(TagSet) tag_set_components() |
|
|
|
{ |
return !sizeof (tags) && !proc_instrs && !string_entities && |
sizeof (imported) == 2 && imported; |
} |
|
static void destroy() |
{ |
catch (changed()); |
if (name) SET_TAG_SET (owner, name, generation); |
} |
|
static void set_name (Configuration new_owner, string new_name) |
|
{ |
if (new_name) { |
object(TagSet)|int old_tag_set = LOOKUP_TAG_SET (owner, name); |
if (objectp (old_tag_set)) { |
|
|
|
|
|
old_tag_set = old_tag_set->generation; |
} |
if (generation <= old_tag_set) generation = old_tag_set; |
owner = new_owner; |
name = new_name; |
SET_TAG_SET (owner, name, this_object()); |
} |
else { |
owner = new_owner; |
name = 0; |
} |
} |
|
static mapping(string:Tag) tags = ([]), proc_instrs; |
|
|
static mapping(string:string) string_entities; |
|
|
|
static TagSet top_tag_set; |
|
|
static int got_local_tags; |
|
|
static array(function(:void)) notify_funcs = ({}); |
|
|
static array(function(Context:void)) prepare_funs; |
|
static multiset(TagSet) dep_tag_sets = set_weak_flag ((<>), 1); |
|
array(function(Context:void)) get_prepare_funs() |
{ |
if (prepare_funs) return prepare_funs; |
array(function(Context:void)) funs = ({}); |
for (int i = sizeof (imported) - 1; i >= 0; i--) |
funs += imported[i]->get_prepare_funs(); |
if (prepare_context) funs += ({prepare_context}); |
|
return funs; |
} |
|
final void call_prepare_funs (Context ctx) |
{ |
if (!prepare_funs) prepare_funs = get_prepare_funs(); |
(prepare_funs -= ({0})) (ctx); |
} |
|
static array(function(Context:void)) eval_finish_funs; |
|
array(function(Context:void)) get_eval_finish_funs() |
{ |
if (eval_finish_funs) return eval_finish_funs; |
array(function(Context:void)) funs = ({}); |
for (int i = sizeof (imported) - 1; i >= 0; i--) |
funs += imported[i]->get_eval_finish_funs(); |
if (eval_finish) funs += ({eval_finish}); |
|
return funs; |
} |
|
void call_eval_finish_funs (Context ctx) |
{ |
if (!eval_finish_funs) eval_finish_funs = get_eval_finish_funs(); |
(eval_finish_funs -= ({0})) (ctx); |
} |
|
static mapping(Tag:Tag) overridden_tag_lookup; |
|
Tag find_overridden_tag (Tag overrider, string overrider_name) |
{ |
if (tags[overrider_name] == overrider) { |
foreach (imported, TagSet tag_set) |
if (object(Tag) overrider = tag_set->get_tag (overrider_name)) |
return overrider; |
} |
else { |
int found = 0; |
foreach (imported, TagSet tag_set) |
if (object(Tag) subtag = tag_set->get_tag (overrider_name)) |
if (found) return subtag; |
else if (subtag == overrider) |
if ((subtag = tag_set->find_overridden_tag ( |
overrider, overrider_name))) |
return subtag; |
else found = 1; |
} |
return 0; |
} |
|
Tag find_overridden_proc_instr (Tag overrider, string overrider_name) |
{ |
if (proc_instrs && proc_instrs[overrider_name] == overrider) { |
foreach (imported, TagSet tag_set) |
if (object(Tag) overrider = tag_set->get_tag (overrider_name, 1)) |
return overrider; |
} |
else { |
int found = 0; |
foreach (imported, TagSet tag_set) |
if (object(Tag) subtag = tag_set->get_tag (overrider_name, 1)) |
if (found) return subtag; |
else if (subtag == overrider) |
if ((subtag = tag_set->find_overridden_proc_instr ( |
overrider, overrider_name))) |
return subtag; |
else found = 1; |
} |
return 0; |
} |
|
static mapping(string:mapping(string:Tag)) plugins, pi_plugins; |
|
void low_get_plugins (string prefix, mapping(string:Tag) res) |
{ |
for (int i = sizeof (imported) - 1; i >= 0; i--) |
imported[i]->low_get_plugins (prefix, res); |
foreach (indices (tags), string name) |
if (name[..sizeof (prefix) - 1] == prefix) { |
Tag tag = tags[name]; |
if (tag->plugin_name) res[[string] tag->plugin_name] = tag; |
} |
|
} |
|
void low_get_pi_plugins (string prefix, mapping(string:Tag) res) |
{ |
for (int i = sizeof (imported) - 1; i >= 0; i--) |
imported[i]->low_get_pi_plugins (prefix, res); |
if (proc_instrs) |
foreach (indices (proc_instrs), string name) |
if (name[..sizeof (prefix) - 1] == prefix) { |
Tag tag = proc_instrs[name]; |
if (tag->plugin_name) res[[string] tag->plugin_name] = tag; |
} |
|
} |
|
static string hash; |
|
array get_hash_data() |
{ |
return ({ |
this_object()->prefix, |
this_object()->prefix_req, |
sort (indices (tags)), |
proc_instrs && sort (indices (proc_instrs)), |
string_entities, |
}) + imported->get_hash_data() + |
({0}) + indices (dep_tag_sets)->get_hash_data(); |
} |
|
string tag_set_component_names() |
{ |
return name || sizeof (imported) && imported->tag_set_component_names() * "+"; |
} |
|
string _sprintf() |
{ |
return "RXML.TagSet(" + |
|
(owner && (owner->is_module ? |
owner->module_local_id() : |
owner->name)) + "," + tag_set_component_names() + ")" + OBJ_COUNT; |
|
} |
|
|
MARK_OBJECT_ONLY; |
|
} |
|
TagSet empty_tag_set; |
|
|
TagSet shared_tag_set (RoxenModule|Configuration owner, string name, void|array(Tag) tags) |
|
|
|
|
{ |
Thread.MutexKey key = all_tag_sets_mutex->lock(); |
if (TagSet tag_set = LOOKUP_TAG_SET (owner, name)) |
if (objectp (tag_set)) |
return tag_set; |
return TagSet (owner, name, tags); |
} |
|
|
class Value |
|
|
{ |
mixed rxml_var_eval (Context ctx, string var, string scope_name, void|Type type) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
mixed val = rxml_const_eval (ctx, var, scope_name, type); |
ctx->set_var(var, val, scope_name); |
return val; |
} |
|
mixed rxml_const_eval (Context ctx, string var, string scope_name, void|Type type); |
|
|
|
optional string format_rxml_backtrace_frame ( |
Context ctx, string var, string scope_name); |
|
|
|
|
|
|
|
|
|
|
|
string _sprintf() {return "RXML.Value";} |
} |
|
class Scope |
|
|
|
|
|
|
{ |
mixed `[] (string var, void|Context ctx, |
void|string scope_name, void|Type type) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{parse_error ("Cannot query variable" + _in_the_scope (scope_name) + ".\n");} |
|
mixed `[]= (string var, mixed val, void|Context ctx, void|string scope_name) |
|
|
|
|
|
|
|
{parse_error ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");} |
|
array(string) _indices (void|Context ctx, void|string scope_name) |
|
|
|
|
|
|
|
{parse_error ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");} |
|
void _m_delete (string var, void|Context ctx, |
void|string scope_name, void|int from_m_delete) |
|
|
|
|
{ |
if (!from_m_delete) |
m_delete (var, ctx, scope_name); |
else |
parse_error ("Cannot delete variable" + _in_the_scope (scope_name) + ".\n"); |
} |
|
void m_delete (string var, void|Context ctx, void|string scope_name) |
|
{_m_delete (var, ctx, scope_name, 1);} |
|
optional string format_rxml_backtrace_frame ( |
Context ctx, string var, string scope_name); |
|
|
|
|
|
|
|
|
|
|
|
private string _in_the_scope (string scope_name) |
{ |
if (scope_name) |
if (scope_name != "_") return " in the scope " + scope_name; |
else return " in the current scope"; |
else return ""; |
} |
|
string _sprintf() {return "RXML.Scope";} |
} |
|
class Context |
|
|
|
|
|
|
|
|
{ |
Frame frame; |
|
|
int frame_depth; |
|
|
|
constant max_frame_depth = 100; |
|
|
RequestID id; |
|
|
mapping(mixed:mixed) misc = ([]); |
|
|
|
|
|
|
|
|
|
|
|
|
int type_check; |
|
|
int error_count; |
|
|
|
|
|
|
TagSet tag_set; |
|
|
#ifdef OLD_RXML_COMPAT |
int compatible_scope = 0; |
|
|
|
|
|
|
|
#endif |
|
void state_update() |
|
|
|
{ |
#ifdef RXML_PCODE_UPDATE_DEBUG |
array a = backtrace(); |
PCODE_UPDATE_MSG ("%O: P-code update by request from %s", |
this_object(), |
describe_backtrace (a[sizeof (a) - 2..sizeof (a) - 2])); |
#endif |
state_updated++; |
} |
|
array(string|int) parse_user_var (string var, void|string|int scope_name) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
#ifdef OLD_RXML_COMPAT |
if (compatible_scope && !intp(scope_name)) |
return ({scope_name || "form", var}); |
#endif |
|
array(string|int) splitted; |
if(has_value(var, "..")) { |
|
|
string coded = replace (var, "\0", "\0\0"); |
if (coded != var) |
splitted = map (replace (coded, "..", "\0p") / ".", |
replace, ({"\0p", "\0\0"}), ({".", "\0"})); |
else |
splitted = map (replace (var, "..", "\0") / ".", replace, "\0", "."); |
} |
else |
splitted = var / "."; |
|
if (stringp (scope_name)) |
splitted = ({scope_name}) + splitted; |
else if (sizeof (splitted) == 1) |
splitted = ({scope_name || "_"}) + splitted; |
|
for (int i = 2; i < sizeof (splitted); i++) |
if (sscanf (splitted[i], "%d%*c", int d) == 1) splitted[i] = d; |
|
return splitted; |
} |
|
local mixed get_var (string|array(string|int) var, void|string scope_name, |
void|Type want_type) |
|
|
|
|
|
|
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
if (arrayp (var) ? !sizeof (var) : !stringp (var)) |
fatal_error ("Invalid variable specifier.\n"); |
#endif |
if (!scope_name) scope_name = "_"; |
if (SCOPE_TYPE vars = scopes[scope_name]) |
return rxml_index (vars, var, scope_name, this_object(), want_type); |
else if (scope_name == "_") parse_error ("No current scope.\n"); |
else parse_error ("Unknown scope %O.\n", scope_name); |
} |
|
mixed user_get_var (string var, void|string scope_name, void|Type want_type) |
|
|
|
|
|
|
|
|
|
{ |
if(!var || !sizeof(var)) return ([])[0]; |
array(string|int) splitted = parse_user_var (var, scope_name); |
return get_var (splitted[1..], splitted[0], want_type); |
} |
|
local mixed set_var (string|array(string|int) var, mixed val, void|string scope_name) |
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
if (arrayp (var) ? !sizeof (var) : !stringp (var)) |
fatal_error ("Invalid variable specifier.\n"); |
#endif |
|
if (!scope_name) scope_name = "_"; |
if (SCOPE_TYPE vars = scopes[scope_name]) { |
string|int index; |
if (arrayp (var)) |
if (sizeof (var) > 1) { |
index = var[-1]; |
array(string|int) path = var[..sizeof (var) - 1]; |
vars = rxml_index (vars, path, scope_name, this_object()); |
scope_name += "." + (array(string)) path * "."; |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({scope_name}) + var)] = val; |
} |
else { |
index = var[0]; |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({scope_name, index}))] = val; |
} |
else { |
index = var; |
if (mapping 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); |
else if (mappingp (vars) || multisetp (vars)) |
return vars[index] = val; |
else if (arrayp (vars)) |
if (intp (index) && index) |
if ((index < 0 ? -index : index) > sizeof (vars)) |
parse_error( "Index %d out of range for array of size %d in %s.\n", |
index, sizeof (val), scope_name ); |
else if (index < 0) |
return vars[index] = val; |
else |
return vars[index - 1] = val; |
else |
parse_error( "Cannot index the array in %s with %s.\n", |
scope_name, format_short (index) ); |
else |
parse_error ("%s is %s which cannot be indexed with %s.\n", |
scope_name, format_short (vars), format_short (index)); |
} |
|
else if (scope_name == "_") parse_error ("No current scope.\n"); |
else parse_error ("Unknown scope %O.\n", scope_name); |
} |
|
mixed user_set_var (string var, mixed val, void|string scope_name) |
|
|
|
|
|
|
|
|
|
{ |
if(!var || !sizeof(var)) parse_error ("No variable specified.\n"); |
array(string|int) splitted = parse_user_var (var, scope_name); |
return set_var(splitted[1..], val, splitted[0]); |
} |
|
local void delete_var (string|array(string|int) var, void|string scope_name) |
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
if (arrayp (var) ? !sizeof (var) : !stringp (var)) |
fatal_error ("Invalid variable specifier.\n"); |
#endif |
|
if (!scope_name) scope_name = "_"; |
if (SCOPE_TYPE vars = scopes[scope_name]) { |
if (arrayp (var)) |
if (sizeof (var) > 1) { |
array(string|int) path = var[..sizeof (var) - 1]; |
vars = rxml_index (vars, path, scope_name, this_object()); |
scope_name += "." + (array(string)) path * "."; |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({scope_name}) + var)] = nil; |
var = var[-1]; |
} |
else { |
var = var[0]; |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({scope_name, var}))] = nil; |
} |
else { |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({scope_name, var}))] = nil; |
} |
|
if (objectp (vars) && vars->_m_delete) |
([object(Scope)] vars)->_m_delete (var, this_object(), scope_name); |
else if (mappingp (vars)) |
m_delete ([mapping(string:mixed)] vars, var); |
else if (multisetp (vars)) |
vars[var] = 0; |
else |
parse_error ("Cannot remove the index %s from the %t in %s.\n", |
format_short (var), vars, scope_name); |
} |
|
else if (scope_name == "_") parse_error ("No current scope.\n"); |
else parse_error ("Unknown scope %O.\n", scope_name); |
} |
|
void user_delete_var (string var, void|string scope_name) |
|
|
|
|
|
|
|
|
|
{ |
if(!var || !sizeof(var)) return; |
array(string|int) splitted = parse_user_var (var, scope_name); |
delete_var(splitted[1..], splitted[0]); |
} |
|
array(string) list_var (void|string scope_name) |
|
|
{ |
if (SCOPE_TYPE vars = scopes[scope_name || "_"]) |
if (objectp (vars)) |
return ([object(Scope)] vars)->_indices (this_object(), scope_name || "_"); |
else |
return indices ([mapping(string:mixed)] vars); |
else if ((<0, "_">)[scope_name]) parse_error ("No current scope.\n"); |
else parse_error ("Unknown scope %O.\n", scope_name); |
} |
|
array(string) list_scopes (void|int list_hidden) |
|
|
{ |
if (list_hidden) |
return indices (scopes) - ({"_"}); |
else |
return indices (scopes) - ({"_", "_internal_"}); |
} |
|
int exist_scope (void|string scope_name) |
|
{ |
return !!scopes[scope_name || "_"]; |
} |
|
#define CLEANUP_VAR_CHG_SCOPE(var_chg, scope_name) do { \ |
foreach (indices (var_chg), mixed encoded_var) \ |
if (stringp (encoded_var)) { \ |
mixed var = decode_value (encoded_var); \ |
if (arrayp (var) && var[0] == scope_name) \ |
m_delete (var_chg, encoded_var); \ |
} \ |
} while (0) |
|
void add_scope (string scope_name, SCOPE_TYPE vars) |
|
|
|
{ |
if (scopes[scope_name]) |
if (scope_name == "_") { |
array(SCOPE_TYPE) hid; |
for (Frame f = frame; f; f = f->up) |
if (array(SCOPE_TYPE) h = hidden[f]) hid = h; |
if (hid) hid[0] = vars; |
else scopes["_"] = vars; |
} |
else { |
Frame outermost; |
for (Frame f = frame; f; f = f->up) |
if (f->scope_name == scope_name) outermost = f; |
if (outermost) hidden[outermost][1] = vars; |
else scopes[scope_name] = vars; |
} |
else scopes[scope_name] = vars; |
|
if (mapping var_chg = misc->variable_changes) { |
CLEANUP_VAR_CHG_SCOPE (var_chg, scope_name); |
var_chg[encode_value_canonic (({scope_name}))] = |
mappingp (vars) ? vars + ([]) : vars; |
} |
} |
|
void extend_scope (string scope_name, SCOPE_TYPE vars) |
|
|
|
|
|
|
|
|
{ |
if (scopes[scope_name]) { |
SCOPE_TYPE oldvars; |
if (scope_name == "_") { |
array(SCOPE_TYPE) hid; |
for (Frame f = frame; f; f = f->up) |
if (array(SCOPE_TYPE) h = hidden[f]) hid = h; |
if (hid) oldvars = hid[0]; |
else oldvars = scopes["_"]; |
} |
else { |
Frame outermost; |
for (Frame f = frame; f; f = f->up) |
if (f->scope_name == scope_name) outermost = f; |
if (outermost) oldvars = hidden[outermost][1]; |
else oldvars = scopes[scope_name]; |
} |
#ifdef DEBUG |
if (!oldvars) fatal_error ("I before e except after c.\n"); |
#endif |
foreach (objectp (vars) ? |
([object(Scope)] vars)->_indices (this_object(), scope_name || "_") : |
indices(vars), string var) |
set_var(var, vars[var], scope_name); |
} |
|
else { |
scopes[scope_name] = vars; |
|
if (mapping var_chg = misc->variable_changes) { |
CLEANUP_VAR_CHG_SCOPE (var_chg, scope_name); |
var_chg[encode_value_canonic (({scope_name}))] = |
mappingp (vars) ? vars + ([]) : vars; |
} |
} |
} |
|
void remove_scope (string scope_name) |
|
{ |
#ifdef MODULE_DEBUG |
if (scope_name == "_") fatal_error ("Cannot remove current scope.\n"); |
#endif |
Frame outermost; |
for (Frame f = frame; f; f = f->up) |
if (f->scope_name == scope_name) outermost = f; |
if (outermost) m_delete (hidden, outermost); |
else m_delete (scopes, scope_name); |
|
if (mapping var_chg = misc->variable_changes) { |
CLEANUP_VAR_CHG_SCOPE (var_chg, scope_name); |
var_chg[encode_value_canonic (({scope_name}))] = 0; |
} |
} |
|
string current_scope() |
|
{ |
if (SCOPE_TYPE vars = scopes["_"]) { |
string scope_name = search (scopes, vars); |
do |
if (scope_name != "_") return scope_name; |
while ((scope_name = search (scopes, vars, scope_name))); |
} |
return 0; |
} |
|
SCOPE_TYPE get_scope (string scope_name) |
|
{ |
return scopes[scope_name]; |
} |
|
void set_misc (mixed index, mixed value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
if (value == nil) m_delete (misc, index); |
else misc[index] = value; |
if (mapping var_chg = misc->variable_changes) { |
if (stringp (index)) index = encode_value_canonic (index); |
var_chg[index] = value; |
} |
} |
|
static int last_internal_var_id = 0; |
|
string alloc_internal_var() |
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
if (!scopes->_internal_) add_scope ("_internal_", ([])); |
return (string) ++last_internal_var_id; |
} |
|
void signal_var_change (string var, void|string scope_name) |
|
|
|
|
|
|
{ |
if (mapping var_chg = misc->variable_changes) { |
if (!scope_name) scope_name = "_"; |
var_chg[encode_value_canonic (({scope_name, var}))] = scopes[scope_name][var]; |
} |
} |
|
void add_runtime_tag (Tag tag) |
|
|
{ |
#ifdef MODULE_DEBUG |
if (tag->plugin_name) |
fatal_error ("Cannot handle plugin tags added at runtime.\n"); |
#endif |
if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags(); |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({0, tag->flags & FLAG_PROC_INSTR ? |
"?" + tag->name : tag->name}))] = tag; |
new_runtime_tags->add_tag (tag); |
} |
|
void remove_runtime_tag (string|Tag tag, void|int proc_instr) |
|
|
|
|
|
{ |
if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags(); |
if (objectp (tag)) { |
proc_instr = tag->flags & FLAG_PROC_INSTR; |
tag = tag->name; |
} |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({0, proc_instr ? "?" + tag : tag}))] = 0; |
new_runtime_tags->remove_tag (tag, proc_instr); |
} |
|
multiset(Tag) get_runtime_tags() |
|
{ |
mapping(string:Tag) tags = runtime_tags; |
if (new_runtime_tags) tags = new_runtime_tags->filter_tags (tags); |
return mkmultiset (values (tags)); |
} |
|
int incomplete_eval() |
|
|
|
{ |
return unwind_state && unwind_state->reason == "streaming"; |
} |
|
void handle_exception (mixed err, PCode|Parser evaluator, void|PCode p_code_error) |
|
|
|
|
{ |
error_count++; |
|
if (objectp (err)) { |
if (err->is_RXML_break_eval) { |
if (err->action == "continue") return; |
Context ctx = RXML_CONTEXT; |
if (ctx->frame) { |
if (stringp (err->target) ? err->target == ctx->frame->scope_name : |
err->target == ctx->frame) |
err->action = "continue"; |
} |
else |
if (err->target) { |
ctx->frame = err->cur_frame; |
err = catch (parse_error ("There is no surrounding frame %s.\n", |
stringp (err->target) ? |
sprintf ("with scope %O", err->target) : |
sprintf ("%O", err->target))); |
ctx->frame = 0; |
handle_exception (err, evaluator, p_code_error); |
} |
throw (err); |
} |
|
else if (err->is_RXML_Backtrace) { |
if (evaluator->report_error && evaluator->recover_errors && |
evaluator->type->free_text) { |
string msg; |
if (tag_set && id && id->conf) { |
msg = err->type == "help" ? err->msg : |
(err->type == "run" ? |
([function(Backtrace,Type:string)] |
tag_set->handle_run_error) : |
([function(Backtrace,Type:string)] |
tag_set->handle_parse_error) |
) ([object(Backtrace)] err, evaluator->type); |
if(!msg) |
msg = describe_error(err); |
} |
else |
msg = err->msg; |
if (evaluator->report_error (msg)) { |
if (p_code_error) { |
CompiledError comp_err = CompiledError (err); |
p_code_error->add (RXML_CONTEXT, comp_err, comp_err); |
} |
return; |
} |
} |
throw (err); |
} |
} |
|
throw_fatal (err); |
} |
|
final array(mixed|PCode) eval_and_compile (Type type, string to_parse, |
void|int stale_safe, |
void|TagSet tag_set_override) |
|
|
|
|
|
|
|
|
{ |
int orig_make_p_code = make_p_code, orig_state_updated = state_updated; |
int orig_top_frame_flags = frame && frame->flags; |
PCODE_UPDATE_MSG ("%O: Saved p-code update count %d before eval_and_compile\n", |
this_object(), orig_state_updated); |
if (!tag_set_override) tag_set_override = tag_set; |
make_p_code = 1; |
Parser parser = type->get_parser ( |
this_object(), tag_set_override, 0, |
stale_safe ? RenewablePCode (type, tag_set) : PCode (type, tag_set)); |
|
mixed res; |
PCode p_code; |
mixed err = catch { |
parser->write_end (to_parse); |
res = parser->eval(); |
p_code = parser->p_code; |
p_code->finish(); |
}; |
|
type->give_back (parser, tag_set_override); |
PCODE_UPDATE_MSG ("%O: Restoring p-code update count from %d to %d " |
"after eval_and_compile\n", |
this_object(), state_updated, orig_state_updated); |
make_p_code = orig_make_p_code, state_updated = orig_state_updated; |
if (frame) |
|
|
|
|
frame->flags &= orig_top_frame_flags | |
~(FLAG_DONT_CACHE_RESULT|FLAG_MAY_CACHE_RESULT); |
|
if (err) throw (err); |
return ({res, p_code}); |
} |
|
|
|
final Parser new_parser (Type top_level_type, void|int _make_p_code) |
|
|
{ |
#ifdef MODULE_DEBUG |
if (in_use || frame) fatal_error ("Context already in use.\n"); |
#endif |
return top_level_type->get_parser (this_object(), tag_set, 0, |
make_p_code = _make_p_code); |
} |
|
#ifdef DEBUG |
private int eval_finished = 0; |
#endif |
|
final void eval_finish() |
|
{ |
FRAME_DEPTH_MSG ("%*s%O eval_finish\n", frame_depth, "", this_object()); |
if (!frame_depth) { |
#ifdef DEBUG |
if (eval_finished) fatal_error ("Context already finished.\n"); |
eval_finished = 1; |
#endif |
if (tag_set) tag_set->call_eval_finish_funs (this_object()); |
if (p_code_comp) p_code_comp->compile(); |
} |
} |
|
mapping(string:SCOPE_TYPE) scopes = ([]); |
|
|
|
mapping(Frame:array(SCOPE_TYPE)) hidden = ([]); |
|
|
|
|
void enter_scope (Frame frame) |
{ |
#ifdef DEBUG |
if (!frame->vars) fatal_error ("Frame has no variables.\n"); |
#endif |
if (string scope_name = [string] frame->scope_name) { |
if (!hidden[frame]) |
hidden[frame] = ({scopes["_"], scopes[scope_name]}); |
scopes["_"] = scopes[scope_name] = [SCOPE_TYPE] frame->vars; |
} |
else { |
if (!hidden[frame]) |
hidden[frame] = ({scopes["_"], 0}); |
scopes["_"] = [SCOPE_TYPE] frame->vars; |
} |
} |
|
void leave_scope (Frame frame) |
{ |
if (array(SCOPE_TYPE) back = hidden[frame]) { |
if (mapping var_chg = misc->variable_changes) { |
CLEANUP_VAR_CHG_SCOPE (var_chg, "_"); |
if (string scope_name = frame->scope_name) |
CLEANUP_VAR_CHG_SCOPE (var_chg, scope_name); |
} |
if (SCOPE_TYPE cur = back[0]) scopes["_"] = cur; |
else m_delete (scopes, "_"); |
if (SCOPE_TYPE named = back[1]) { |
#ifdef MODULE_DEBUG |
if (!stringp (frame->scope_name)) |
fatal_error ("Scope named changed to %O during parsing.\n", frame->scope_name); |
#endif |
scopes[[string] frame->scope_name] = named; |
} |
else m_delete (scopes, [string] frame->scope_name); |
m_delete (hidden, frame); |
} |
} |
|
#define ENTER_SCOPE(ctx, frame) \ |
(frame->vars && frame->vars != ctx->scopes["_"] && ctx->enter_scope (frame)) |
#define LEAVE_SCOPE(ctx, frame) \ |
(frame->vars && ctx->leave_scope (frame)) |
|
mapping(string:Tag) runtime_tags = ([]); |
|
|
|
void direct_add_runtime_tag (string name, Tag tag) |
{ |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({0, name}))] = tag; |
runtime_tags[name] = tag; |
} |
|
void direct_remove_runtime_tag (string name) |
{ |
if (mapping var_chg = misc->variable_changes) |
var_chg[encode_value_canonic (({0, name}))] = 0; |
m_delete (runtime_tags, name); |
} |
|
NewRuntimeTags new_runtime_tags; |
|
|
|
int make_p_code; |
|
|
int state_updated; |
|
|
|
PikeCompile p_code_comp; |
|
|
|
static void create (void|TagSet _tag_set, void|RequestID _id) |
|
{ |
tag_set = _tag_set || empty_tag_set; |
id = _id; |
#ifdef RXML_OBJ_DEBUG |
__object_marker->create (this_object()); |
#endif |
} |
|
UNWIND_STATE unwind_state; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mapping id_defines; |
|
|
|
|
|
MARK_OBJECT_ONLY; |
|
|
string _sprintf() {return "RXML.Context" + OBJ_COUNT;} |
|
#ifdef MODULE_DEBUG |
#if constant (thread_create) |
Thread.Thread in_use; |
#else |
int in_use; |
#endif |
#endif |
} |
|
static class NewRuntimeTags |
|
{ |
static mapping(string:Tag) add_tags; |
static mapping(string:int|string) remove_tags; |
|
void add_tag (Tag tag) |
{ |
if (!add_tags) add_tags = ([]); |
if (tag->flags & FLAG_PROC_INSTR) { |
add_tags["?" + tag->name] = tag; |
|
if (remove_tags) m_delete (remove_tags, "?" + tag->name); |
} |
else { |
add_tags[tag->name] = tag; |
if (remove_tags) m_delete (remove_tags, tag->name); |
} |
} |
|
void remove_tag (string name, int proc_instr) |
{ |
if (!remove_tags) remove_tags = ([]); |
if (proc_instr) remove_tags["?" + name] = name; |
else remove_tags[name] = 1; |
} |
|
array(Tag) added_tags() |
{ |
if (!add_tags) return ({}); |
if (remove_tags) return values (add_tags - remove_tags); |
return values (add_tags); |
} |
|
array(string) removed_tags() |
{ |
return remove_tags ? indices (filter (remove_tags, intp)) : ({}); |
} |
|
array(string) removed_pi_tags() |
{ |
return remove_tags ? values (remove_tags) - ({1}) : ({}); |
} |
|
mapping(string:Tag) filter_tags (mapping(string:Tag) tags) |
{ |
if (add_tags) tags |= add_tags; |
if (remove_tags) tags -= remove_tags; |
return tags; |
} |
} |
|
static class BreakEval (Frame|string target) |
|
{ |
constant is_RXML_BreakEval = 1; |
string action = "break"; |
Frame cur_frame = RXML_CONTEXT->frame; |
} |
|
class Backtrace |
|
{ |
constant is_generic_error = 1; |
constant is_RXML_Backtrace = 1; |
|
string type; |
string msg; |
Context context; |
array(Frame) frames; |
array(mapping(string:mixed)) args; |
string current_var; |
array backtrace; |
|
static void create (void|string _type, void|string _msg, void|Context _context, |
void|array _backtrace) |
{ |
type = _type; |
msg = _msg; |
if (context = _context || RXML_CONTEXT) { |
frames = allocate (context->frame_depth); |
args = allocate (context->frame_depth); |
Frame frame = context->frame; |
int i = 0; |
for (; frame; i++, frame = frame->up) { |
if (i >= sizeof (frames)) { |
frames += allocate (sizeof (frames) + 1); |
args += allocate (sizeof (args) + 1); |
} |
frames[i] = frame; |
args[i] = frame->args; |
} |
frames = frames[..i - 1]; |
args = args[..i - 1]; |
} |
if (_backtrace) backtrace = _backtrace; |
else { |
backtrace = predef::backtrace(); |
backtrace = backtrace[..sizeof (backtrace) - 2]; |
} |
} |
|
string describe_rxml_backtrace (void|int no_msg) |
|
{ |
String.Buffer txt = String.Buffer(); |
function(string...:void) add = txt->add; |
if (!no_msg) add ("RXML", type ? " " + type : "", " error"); |
if (context) { |
if (!no_msg) add (": ", msg || "(no error message)\n"); |
if (current_var && current_var != "") add (" | ", current_var, "\n"); |
for (int i = 0; i < sizeof (frames); i++) { |
Frame f = frames[i]; |
string name; |
if (f->format_rxml_backtrace_frame) { |
string res = f->format_rxml_backtrace_frame(); |
if (res != "") add (" | ", res, "\n"); |
} |
else { |
if (f->tag) name = f->tag->name; |
|
else name = "(unknown)"; |
if (f->flags & FLAG_PROC_INSTR) |
add (" | <?", name, "?>\n"); |
else { |
add (" | <", name); |
mapping(string:mixed) argmap = args[i]; |
if (mappingp (argmap)) |
foreach (sort (indices (argmap)), string arg) { |
mixed val = argmap[arg]; |
add (" ", arg, "="); |
if (arrayp (val)) add (map (val, error_print_val) * ","); |
else add (error_print_val (val)); |
} |
else add (" (no argmap)"); |
add (">\n"); |
} |
} |
} |
} |
else |
if (!no_msg) add (" (no context): ", msg || "(no error message)\n"); |
return txt->get(); |
} |
|
private string error_print_val (mixed val) |
{ |
if (arrayp (val)) return "array"; |
else if (mappingp (val)) return "mapping"; |
else if (multisetp (val)) return "multiset"; |
else return sprintf ("%O", val); |
} |
|
string|array `[] (int i) |
{ |
switch (i) { |
case 0: return describe_rxml_backtrace(); |
case 1: return backtrace; |
} |
} |
|
mixed `[]= (int i, mixed val) |
{ |
if (i == 0 && stringp (val)) { |
|
|
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 || "") + ")";} |
} |
|
|
|
|
final void set_context (Context ctx) {SET_RXML_CONTEXT (ctx);} |
|
final Context get_context() {return [object(Context)] RXML_CONTEXT;} |
|
|
|
|
|
|
|
|
#if defined (MODULE_DEBUG) && constant (thread_create) |
|
|
|
#define ENTER_CONTEXT(ctx) \ |
Context __old_ctx = RXML_CONTEXT; \ |
SET_RXML_CONTEXT (ctx); \ |
if (ctx) { \ |
if (ctx->in_use && ctx->in_use != this_thread()) \ |
fatal_error ("Attempt to use context asynchronously.\n"); \ |
ctx->in_use = this_thread(); \ |
} |
|
#define LEAVE_CONTEXT() \ |
if (Context ctx = RXML_CONTEXT) \ |
if (__old_ctx != ctx) ctx->in_use = 0; \ |
SET_RXML_CONTEXT (__old_ctx); |
|
#else |
|
#define ENTER_CONTEXT(ctx) \ |
Context __old_ctx = RXML_CONTEXT; \ |
SET_RXML_CONTEXT (ctx); |
|
#define LEAVE_CONTEXT() \ |
SET_RXML_CONTEXT (__old_ctx); |
|
#endif |
|
|
|
|
constant FLAG_NONE = 0x00000000; |
|
|
constant FLAG_DEBUG = 0x40000000; |
|
|