|
|
|
|
|
|
|
|
static object Roxen; |
class RequestID { }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h> |
#include <request_trace.h> |
|
#ifdef RXML_OBJ_DEBUG |
# define MARK_OBJECT \ |
Debug.ObjectMarker __object_marker = Debug.ObjectMarker (this_object()) |
# define MARK_OBJECT_ONLY \ |
Debug.ObjectMarker __object_marker = Debug.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_marker = (["count": ++all_constants()->_obj_count]) |
# define MARK_OBJECT_ONLY \ |
mapping __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) (frame)->tag_debug ("%O: %s", (frame), (msg)) |
#else |
# define TAG_DEBUG(frame, msg) 0 |
#endif |
|
#define HASH_INT2(m, n) (n < 65536 ? (m << 16) + n : sprintf ("%x,%x", m, n)) |
|
|
class Tag |
|
{ |
constant is_RXML_Tag = 1; |
|
|
|
|
|
|
int flags; |
|
|
|
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_xml, t_html, t_text}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline final object `() (mapping(string:mixed) args, void|mixed content) |
|
|
|
{ |
Tag this = this_object(); |
object frame = ([function(:object)] this->Frame)(); |
frame->tag = this; |
frame->flags = flags; |
frame->args = args; |
frame->content = zero_type (content) ? nil : content; |
frame->result = nil; |
return frame; |
} |
|
int eval_args (mapping(string:mixed) args, void|int dont_throw, void|Context ctx) |
|
|
|
|
|
|
|
|
{ |
|
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 (atypes), string arg) |
args[arg] = atypes[arg]->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; |
} |
|
|
|
array _handle_tag (TagSetParser parser, mapping(string:string) args, |
void|string content) |
|
|
|
{ |
|
|
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; |
do { |
if (mapping(string:mixed)|mapping(object:array) ustate = ctx->unwind_state) |
if (ustate[parser]) { |
frame = [object] ustate[parser][0]; |
m_delete (ustate, parser); |
if (!sizeof (ustate)) ctx->unwind_state = 0; |
break; |
} |
frame = `() (args, nil); |
TAG_DEBUG (frame, "New frame\n"); |
} while (0); |
|
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(); |
} |
|
mixed err = catch { |
frame->_eval (parser, args, content); |
mixed res; |
if ((res = frame->result) == nil) return ({}); |
if (frame->result_type->name != parser->type->name) { |
TAG_DEBUG (frame, sprintf ( |
"Converting result from %s to %s of surrounding content\n", |
frame->result_type->name, parser->type->name)); |
res = parser->type->encode (res, frame->result_type); |
} |
return ({res}); |
}; |
|
if (objectp (err) && ([object] err)->thrown_at_unwind) { |
mapping(string:mixed)|mapping(object:array) ustate = ctx->unwind_state; |
if (!ustate) ustate = ctx->unwind_state = ([]); |
#ifdef DEBUG |
if (err != frame) |
fatal_error ("Internal error: Unexpected unwind object catched.\n"); |
if (ustate[parser]) |
fatal_error ("Internal error: Clobbering unwind state for parser.\n"); |
#endif |
ustate[parser] = ({err}); |
throw (err = parser); |
} |
else { |
ctx->handle_exception (err, parser); |
return ({}); |
} |
} |
|
array _handle_pi_tag (TagSetParser parser, string content) |
|
|
{ |
sscanf (content, "%[ \t\n\r]%s", string ws, string rest); |
if (ws == "" && rest != "") |
|
|
return 0; |
return _handle_tag (parser, 0, content); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
{ |
string name; |
|
|
string prefix; |
|
|
|
|
int prefix_req; |
|
|
array(TagSet) imported = ({}); |
|
|
|
|
function(Context:void) prepare_context; |
|
|
|
|
|
int generation = 1; |
|
|
|
int id_number; |
|
|
static void create (string _name, void|array(Tag) _tags) |
|
{ |
id_number = ++tag_set_count; |
name = _name; |
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 (!functionp (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 (!functionp (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 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(); |
} |
|
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 int has_effective_tags (TagSet tset) |
|
{ |
return tset == top_tag_set && !got_local_tags; |
} |
|
local mixed `->= (string var, mixed val) |
{ |
switch (var) { |
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);} |
|
Parser `() (Type top_level_type, void|RequestID id) |
|
|
|
{ |
Context ctx = Context (this_object(), id); |
if (!prepare_funs) prepare_funs = get_prepare_funs(); |
(prepare_funs -= ({0})) (ctx); |
return ctx->new_parser (top_level_type); |
} |
|
void changed() |
|
|
{ |
generation++; |
prepare_funs = 0; |
overridden_tag_lookup = 0; |
plugins = pi_plugins = 0; |
(notify_funcs -= ({0}))(); |
set_weak_flag (notify_funcs, 1); |
got_local_tags = sizeof (tags) || (proc_instrs && sizeof (proc_instrs)); |
} |
|
|
|
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); |
} |
|
void destroy() |
{ |
catch (changed()); |
} |
|
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; |
|
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; |
} |
|
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_proc_instr (overrider_name)) |
return overrider; |
} |
else { |
int found = 0; |
foreach (imported, TagSet tag_set) |
if (object(Tag) subtag = tag_set->get_proc_instr (overrider_name)) |
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; |
} |
|
void call_prepare_funs (Context ctx) |
|
{ |
if (!prepare_funs) prepare_funs = get_prepare_funs(); |
(prepare_funs -= ({0})) (ctx); |
} |
|
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; |
} |
|
} |
|
string _sprintf() |
{ |
return sprintf ("RXML.TagSet(%O,%d)%s", name, id_number, OBJ_COUNT); |
} |
|
MARK_OBJECT_ONLY; |
} |
|
TagSet empty_tag_set; |
|
|
|
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); |
|
|
|
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) |
|
|
|
{ |
if (m_delete != local::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);} |
|
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";} |
} |
|
#define SCOPE_TYPE mapping(string:mixed)|object(Scope) |
|
class Context |
|
|
|
|
|
|
|
{ |
Frame frame; |
|
|
int frame_depth; |
|
|
int max_frame_depth = 100; |
|
|
RequestID id; |
|
|
int type_check; |
|
|
int error_count; |
|
|
TagSet tag_set; |
|
|
#ifdef OLD_RXML_COMPAT |
int compatible_scope = 0; |
|
|
|
#endif |
|
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)) |
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)) |
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]; |
var = var[..sizeof (var) - 1]; |
vars = rxml_index (vars, var, scope_name, this_object()); |
scope_name += "." + (array(string)) var * "."; |
} |
else index = var[0]; |
else index = var; |
|
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 %O.\n", scope_name, index ); |
else |
parse_error ("%s is %O which cannot be indexed with %O.\n", |
scope_name, vars, 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)) |
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) { |
string|int last = var[-1]; |
var = var[..sizeof (var) - 1]; |
vars = rxml_index (vars, var, scope_name, this_object()); |
scope_name += "." + (array(string)) var * "."; |
var = last; |
} |
else var = var[0]; |
|
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 %O from the %t in %s.\n", |
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() |
|
{ |
return indices (scopes) - ({"_"}); |
} |
|
int exist_scope (void|string scope_name) |
|
{ |
return !!scopes[scope_name || "_"]; |
} |
|
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; |
} |
|
int 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 ("Internal error: I before e except after c.\n"); |
#endif |
if (!mappingp(vars)) { |
return 0; |
} |
foreach (indices(vars), string var) |
set_var(var, vars[var], scope_name); |
} |
else scopes[scope_name] = vars; |
return 1; |
} |
|
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); |
} |
|
string current_scope() |
|
{ |
if (SCOPE_TYPE vars = scopes["_"]) { |
string scope_name; |
while (scope_name = search (scopes, vars, scope_name)) |
if (scope_name != "_") return scope_name; |
} |
return 0; |
} |
|
void add_runtime_tag (Tag tag) |
|
|
{ |
#ifdef MODULE_DEBUG |
if (tag->plugin_name) |
fatal_error ("Can't currently handle adding of plugin tags at runtime.\n"); |
#endif |
if (!new_runtime_tags) new_runtime_tags = NewRuntimeTags(); |
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)) tag = tag->name; |
new_runtime_tags->remove_tag (tag); |
} |
|
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)); |
} |
|
void handle_exception (mixed err, PCode|Parser evaluator) |
|
|
{ |
error_count++; |
if (objectp (err) && err->is_RXML_Backtrace) { |
evaluator->error_count++; |
if (evaluator->report_error && evaluator->recover_errors && |
evaluator->type->free_text) { |
string msg; |
if (id && id->conf) |
msg = err->type == "help" ? err->msg : |
(err->type == "run" ? |
([function(Backtrace,Type:string)] |
([object] id->conf)->handle_run_error) : |
([function(Backtrace,Type:string)] |
([object] id->conf)->handle_parse_error) |
) ([object(Backtrace)] err, evaluator->type); |
else |
msg = err->msg; |
if (evaluator->report_error (msg)) |
return; |
} |
throw (err); |
} |
else throw_fatal (err); |
} |
|
|
|
string current_var; |
|
|
Parser new_parser (Type top_level_type) |
|
|
{ |
#ifdef MODULE_DEBUG |
if (in_use || frame) fatal_error ("Context already in use.\n"); |
#endif |
return top_level_type->get_parser (this_object()); |
} |
|
mapping(string:SCOPE_TYPE) scopes = ([]); |
|
|
|
mapping(Frame:array(SCOPE_TYPE)) hidden = ([]); |
|
|
|
|
void enter_scope (Frame frame) |
{ |
#ifdef DEBUG |
if (!frame->vars) fatal_error ("Internal 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 (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 && ctx->enter_scope (frame)) |
#define LEAVE_SCOPE(ctx, frame) (frame->vars && ctx->leave_scope (frame)) |
|
mapping(string:Tag) runtime_tags = ([]); |
|
|
|
NewRuntimeTags new_runtime_tags; |
|
|
|
void create (TagSet _tag_set, void|RequestID _id) |
|
{ |
tag_set = _tag_set; |
id = _id; |
#ifdef RXML_OBJ_DEBUG |
__object_marker->create (this_object()); |
#endif |
} |
|
mapping(string:mixed)|mapping(object:array) unwind_state; |
|
|
|
|
|
|
|
|
|
|
|
|
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; |
} |
} |
|
class Backtrace |
|
{ |
constant is_generic_error = 1; |
constant is_RXML_Backtrace = 1; |
|
string type; |
string msg; |
Context context; |
Frame frame; |
string current_var; |
array backtrace; |
|
void create (void|string _type, void|string _msg, void|Context _context, |
void|array _backtrace) |
{ |
type = _type; |
msg = _msg; |
if (context = _context || get_context()) { |
frame = context->frame; |
current_var = context->current_var; |
} |
if (_backtrace) backtrace = _backtrace; |
else { |
backtrace = predef::backtrace(); |
backtrace = backtrace[..sizeof (backtrace) - 2]; |
} |
} |
|
string describe_rxml_backtrace (void|int no_msg) |
|
{ |
string txt = no_msg ? "" : "RXML" + (type ? " " + type : "") + " error"; |
if (context) { |
if (!no_msg) txt += ": " + (msg || "(no error message)\n"); |
txt += current_var ? " | &" + 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"; |
else { |
txt += " | <" + name; |
if (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); |
} |
else txt += " (no argmap)"; |
txt += ">\n"; |
} |
} |
} |
else |
if (!no_msg) txt += " (no context): " + (msg || "(no error message)\n"); |
return txt; |
} |
|
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; |
} |
} |
|
string _sprintf() {return "RXML.Backtrace(" + (type || "") + ")";} |
} |
|
|
|
|
|
|
#if constant (thread_create) |
private Thread.Local _context = Thread.Local(); |
local void set_context (Context ctx) {_context->set (ctx);} |
local Context get_context() {return [object(Context)] _context->get();} |
#else |
private Context _context; |
local void set_context (Context ctx) {_context = ctx;} |
local Context get_context() {return _context;} |
#endif |
|
#if defined (MODULE_DEBUG) && constant (thread_create) |
|
|
|
#define ENTER_CONTEXT(ctx) \ |
Context __old_ctx = get_context(); \ |
set_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 = get_context()) \ |
if (__old_ctx != ctx) ctx->in_use = 0; \ |
set_context (__old_ctx); |
|
#else |
|
#define ENTER_CONTEXT(ctx) \ |
Context __old_ctx = get_context(); \ |
set_context (ctx); |
|
#define LEAVE_CONTEXT() \ |
set_context (__old_ctx); |
|
#endif |
|
|
|
|
constant FLAG_NONE = 0x00000000; |
|
|
constant FLAG_DEBUG = 0x40000000; |
|
|
|
|
|
|
|
constant FLAG_PROC_INSTR = 0x00000010; |
|
|
|
|
|
constant FLAG_EMPTY_ELEMENT = 0x00000001; |
|
|
|
|
|
constant FLAG_COMPAT_PARSE = 0x00000002; |
|
|
|
|
|
constant FLAG_NO_PREFIX = 0x00000004; |
|
|
constant FLAG_SOCKET_TAG = 0x00000008; |
|
|
|
constant FLAG_DONT_PREPARSE = 0x00000040; |
|
|
|
|
|
constant FLAG_POSTPARSE = 0x00000080; |
|
|
|
|
|
constant FLAG_PARENT_SCOPE = 0x00000100; |
|
|
|
constant FLAG_NO_IMPLICIT_ARGS = 0x00000200; |
|
|
|
constant FLAG_STREAM_RESULT = 0x00000400; |
|
|
|
constant FLAG_STREAM_CONTENT = 0x00000800; |
|
|
|
|
|
|
|
|
constant FLAG_STREAM = FLAG_STREAM_RESULT | FLAG_STREAM_CONTENT; |
|
constant FLAG_UNPARSED = 0x00001000; |
|
|
|
|
|
constant FLAG_DONT_RECOVER = 0x00002000; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constant FLAG_DONT_REPORT_ERRORS = FLAG_DONT_RECOVER; |
|
constant FLAG_RAW_ARGS = 0x00004000; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constant FLAG_CACHE_DIFF_ARGS = 0x00010000; |
|
|
|
constant FLAG_CACHE_DIFF_CONTENT = 0x00020000; |
|
|
constant FLAG_CACHE_DIFF_RESULT_TYPE = 0x00040000; |
|
|
|
constant FLAG_CACHE_DIFF_VARS = 0x00080000; |
|
|
|
|
constant FLAG_CACHE_DIFF_TAG_INSTANCE = 0x00100000; |
|
|
|
constant FLAG_CACHE_EXECUTE_RESULT = 0x00200000; |
|
|
|
|
class Frame |
|
|
|
|
|
{ |
constant is_RXML_Frame = 1; |
constant thrown_at_unwind = 1; |
|
|
|
Frame up; |
|
|
|
|
Tag tag; |
|
|
int flags; |
|
|
|
mapping(string:mixed) args; |
|
|
|
|
|
Type content_type; |
|
|
mixed content = nil; |
|
|
|
Type result_type; |
|
|
|
|
|
|
|
|
|
|
|
mixed result = nil; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
optional array cached_return (Context ctx, void|mixed piece); |
|
|
|
|
|
|
|
|
final mixed get_var (string var, void|string scope_name, void|Type want_type) |
|
{ |
return get_context()->get_var (var, scope_name, want_type); |
} |
|
final mixed set_var (string var, mixed val, void|string scope_name) |
|
{ |
return get_context()->set_var (var, val, scope_name); |
} |
|
final void delete_var (string var, void|string scope_name) |
|
{ |
get_context()->delete_var (var, scope_name); |
} |
|
void run_error (string msg, mixed... args) |
|
{ |
_run_error (msg, @args); |
} |
|
void parse_error (string msg, mixed... args) |
|
{ |
_parse_error (msg, @args); |
} |
|
void tag_debug (string msg, mixed... args) |
|
|
{ |
if (flags & FLAG_DEBUG) report_debug (msg, @args); |
} |
|
void terminate() |
|
|
{ |
fatal_error ("FIXME\n"); |
} |
|
void suspend() |
|
|
|
|
|
{ |
fatal_error ("FIXME\n"); |
} |
|
void resume() |
|
|
{ |
fatal_error ("FIXME\n"); |
} |
|
mapping(string:Tag) get_plugins() |
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
if (!(tag->flags & FLAG_SOCKET_TAG)) |
fatal_error ("This tag is not a socket tag.\n"); |
#endif |
return get_context()->tag_set->get_plugins (tag->name, tag->flags & FLAG_PROC_INSTR); |
} |
|
final Tag get_overridden_tag() |
|
{ |
return get_context()->tag_set->get_overridden_tag (tag); |
} |
|
Frame|string propagate_tag (void|mapping(string:string) args, void|string content) |
|
|
|
|
|
|
|
|
|
{ |
if (object(Tag) overridden = get_overridden_tag()) { |
Frame frame; |
if (!args) { |
#ifdef MODULE_DEBUG |
if (zero_type (this_object()->raw_tag_text)) |
fatal_error ("The variable raw_tag_text must be defined.\n"); |
if (!stringp (this_object()->raw_tag_text)) |
fatal_error ("raw_tag_text must have a string value.\n"); |
#endif |
sscanf (this_object()->raw_tag_text, "%*[^ \t\n\r]%s", string str); |
#ifdef DEBUG |
if (!str || !sizeof (str) || str[-1] != '>') |
fatal_error ("Failed to parse tag args from %O.\n", |
this_object()->raw_tag_text); |
#endif |
args = Parser_HTML()->parse_tag_args (str); |
#ifdef DEBUG |
if (!mappingp (args)) fatal_error ("Failed to parse tag args from %O.\n", |
this_object()->raw_tag_text); |
#endif |
} |
frame = overridden (args, content || ""); |
frame->flags |= FLAG_UNPARSED; |
return frame; |
} |
|
else |
if (args) return result_type->format_tag (tag, args, content); |
else { |
#ifdef MODULE_DEBUG |
if (zero_type (this_object()->raw_tag_text)) |
fatal_error ("The variable raw_tag_text must be defined.\n"); |
if (!stringp (this_object()->raw_tag_text)) |
fatal_error ("raw_tag_text must have a string value.\n"); |
#endif |
if (flags & FLAG_PROC_INSTR) |
return this_object()->raw_tag_text; |
else { |
#ifdef MODULE_DEBUG |
if (mixed err = catch { |
#endif |
return t_xml (PEnt)->eval (this_object()->raw_tag_text, |
get_context(), empty_tag_set); |
#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 |
} |
} |
} |
|
|
|
#ifdef DEBUG |
# define THIS_TAG_TOP_DEBUG(msg) tag_debug ("%O: %s", this_object(), (msg)) |
# define THIS_TAG_DEBUG(msg) tag_debug ("%O: %s", this_object(), (msg)) |
# define THIS_TAG_DEBUG_ENTER_SCOPE(ctx, this, msg) \ |
if (this->vars && ctx->scopes["_"] != this->vars) THIS_TAG_DEBUG (msg) |
# define THIS_TAG_DEBUG_LEAVE_SCOPE(ctx, this, msg) \ |
if (this->vars && ctx->scopes["_"] == this->vars) THIS_TAG_DEBUG (msg) |
#else |
# define THIS_TAG_TOP_DEBUG(msg) 0 |
# define THIS_TAG_DEBUG(msg) 0 |
# define THIS_TAG_DEBUG_ENTER_SCOPE(ctx, this, msg) 0 |
# define THIS_TAG_DEBUG_LEAVE_SCOPE(ctx, this, msg) 0 |
#endif |
|
mixed _exec_array (TagSetParser parser, array exec, int parent_scope) |
{ |
Frame this = this_object(); |
Context ctx = parser->context; |
int i = 0; |
mixed res = nil; |
Parser subparser = 0; |
|
mixed err = catch { |
if (parent_scope) { |
THIS_TAG_DEBUG_LEAVE_SCOPE (ctx, this, "Exec: Temporary leaving scope\n"); |
LEAVE_SCOPE (ctx, this); |
} |
|
for (; i < sizeof (exec); i++) { |
mixed elem = exec[i], piece = nil; |
|
switch (sprintf ("%t", elem)) { |
case "string": |
if (result_type->_parser_prog == PNone) { |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: String\n", i)); |
piece = elem; |
} |
else { |
subparser = result_type->get_parser (ctx, 0, parser); |
if (parser->recover_errors && !(flags & FLAG_DONT_RECOVER)) |
subparser->recover_errors = 1; |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Parsing string with %O\n", |
i, subparser)); |
subparser->finish ([string] elem); |
piece = subparser->eval(); |
subparser = 0; |
} |
break; |
|
case "mapping": |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Header mapping\n", i)); |
fatal_error ("Header mappings not yet implemented.\n"); |
break; |
|
case "multiset": |
if (sizeof ([multiset] elem) == 1) { |
piece = ((array) elem)[0]; |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Verbatim %t value\n", i, piece)); |
} |
else if (sizeof ([multiset] elem) > 1) |
fatal_error (sizeof ([multiset] elem) + |
" values in multiset in exec array.\n"); |
else fatal_error ("No value in multiset in exec array.\n"); |
break; |
|
default: |
if (objectp (elem)) |
|
|
if (([object] elem)->is_RXML_Frame) { |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Evaluating frame %O\n", |
i, ([object] elem))); |
([object(Frame)] elem)->_eval (parser); |
piece = ([object(Frame)] elem)->result; |
} |
else if (([object] elem)->is_RXML_Parser) { |
|
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Continuing eval of frame %O\n", |
i, ([object] elem))); |
([object(Parser)] elem)->finish(); |
piece = ([object(Parser)] elem)->eval(); |
} |
else |
fatal_error ("File objects not yet implemented.\n"); |
else |
fatal_error ("Invalid type %t in exec array.\n", elem); |
} |
|
if (result_type->sequential) { |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Adding %t to result\n", i, piece)); |
res += piece; |
} |
else if (piece != nil) { |
THIS_TAG_DEBUG (sprintf ("Exec[%d]: Setting result to %t\n", i, piece)); |
result = res = piece; |
} |
} |
|
if (result_type->sequential) result += res; |
if (parent_scope) { |
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this, "Exec: Reentering scope\n"); |
ENTER_SCOPE (ctx, this); |
} |
return res; |
}; |
|
if (parent_scope) { |
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this, "Exec: Reentering scope\n"); |
ENTER_SCOPE (ctx, this); |
} |
if (result_type->sequential) result += res; |
|
if (objectp (err) && ([object] err)->thrown_at_unwind) { |
THIS_TAG_DEBUG (sprintf ("Exec: Interrupted at position %d\n", i)); |
mapping(string:mixed)|mapping(object:array) ustate; |
if ((ustate = ctx->unwind_state) && !zero_type (ustate->stream_piece)) { |
|
if (result_type->name != parser->type->name) |
res = parser->type->encode (res, result_type); |
if (result_type->sequential) |
ustate->stream_piece = res + ustate->stream_piece; |
else if (ustate->stream_piece == nil) |
ustate->stream_piece = res; |
} |
ustate->exec_left = exec[i..]; |
if (subparser) |
|
|
|
([array] ustate->exec_left)[0] = subparser; |
throw (err); |
} |
throw_fatal (err); |
} |
|
private void _handle_runtime_tags (Context ctx, TagSetParser parser) |
{ |
|
array(Tag) arr_add_tags = ctx->new_runtime_tags->added_tags(); |
array(string) arr_rem_tags = ctx->new_runtime_tags->removed_tags(); |
array(string) arr_rem_pi_tags = ctx->new_runtime_tags->removed_pi_tags(); |
for (Parser p = parser; p; p = p->_parent) |
if (p->tag_set_eval && !p->_local_tag_set && p->add_runtime_tag) { |
foreach (arr_add_tags, Tag tag) { |
THIS_TAG_DEBUG (sprintf ("Adding runtime tag %O\n", tag)); |
([object(TagSetParser)] p)->add_runtime_tag (tag); |
} |
foreach (arr_rem_tags, string tag) { |
THIS_TAG_DEBUG (sprintf ("Removing runtime tag %s\n", tag)); |
([object(TagSetParser)] p)->remove_runtime_tag (tag); |
} |
foreach (arr_rem_pi_tags, string tag) { |
THIS_TAG_DEBUG (sprintf ("Removing runtime tag %s\n", tag)); |
([object(TagSetParser)] p)->remove_runtime_tag (tag, 1); |
} |
} |
ctx->runtime_tags = ctx->new_runtime_tags->filter_tags (ctx->runtime_tags); |
ctx->new_runtime_tags = 0; |
} |
|
void _eval (TagSetParser parser, |
void|mapping(string:string) raw_args, |
void|string raw_content) |
|
|
{ |
Frame this = this_object(); |
Context ctx = parser->context; |
RequestID id = ctx->id; |
|
|
|
#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; |
TagSet orig_tag_set; |
|
|
#define PRE_INIT_ERROR(X) (ctx->frame = this, fatal_error (X)) |
#ifdef DEBUG |
|
if (ctx != get_context()) |
PRE_INIT_ERROR ("Internal error: Context not current.\n"); |
if (!parser->tag_set_eval) |
PRE_INIT_ERROR ("Internal 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"); |
#endif |
|
if (array state = ctx->unwind_state && ctx->unwind_state[this]) { |
#ifdef DEBUG |
if (!up) |
PRE_INIT_ERROR ("Internal error: Resuming frame without up pointer.\n"); |
if (raw_args || raw_content) |
PRE_INIT_ERROR ("Internal 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")); |
} |
else { |
if (flags & FLAG_UNPARSED) { |
#ifdef DEBUG |
if (raw_args || raw_content) |
PRE_INIT_ERROR ("Internal 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"); |
#endif |
THIS_TAG_TOP_DEBUG ("Evaluating unparsed\n"); |
} |
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"); |
#endif |
up = ctx->frame; |
piece = nil; |
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; |
|
do { |
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 ( |
Backtrace ("help", help, ctx), parser); |
break; |
} |
|
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; |
} |
#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 |
if (sizeof (raw_args)) { |
|
mapping(string:Type) atypes = raw_args & tag->req_arg_types; |
if (sizeof (atypes) < sizeof (tag->req_arg_types)) { |
array(string) missing = sort (indices (tag->req_arg_types - atypes)); |
parse_error ("Required " + |
(sizeof (missing) > 1 ? |
"arguments " + String.implode_nicely (missing) + " are" : |
"argument " + missing[0] + " is") + " missing.\n"); |
} |
atypes += raw_args & tag->opt_arg_types; |
#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, parser); |
THIS_TAG_DEBUG (sprintf ("Evaluating argument %O with %O\n", arg, p)); |
p->finish (raw_args[arg]); |
raw_args[arg] = p->eval(); |
} |
#else |
raw_args[arg] = (atypes[arg] || tag->def_arg_type)-> |
eval (raw_args[arg], ctx, 0, parser, 1); |
#endif |
} |
#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 |
} |
args = raw_args; |
} |
#ifdef MODULE_DEBUG |
else if (!args && !(flags & FLAG_PROC_INSTR)) fatal_error ("args not set.\n"); |
#endif |
|
if (!zero_type (this->parent_frame)) |
if (up->local_tags && up->local_tags->has_tag (tag)) { |
THIS_TAG_DEBUG (sprintf ("Setting parent_frame to %O from local_tags\n", up)); |
this->parent_frame = up; |
} |
else |
for (Frame f = up; f; f = f->up) |
if (f->additional_tags && f->additional_tags->has_tag (tag)) { |
THIS_TAG_DEBUG (sprintf ("Setting parent_frame to %O " |
"from additional_tags\n", f)); |
this->parent_frame = f; |
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 (sprintf ("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; |
} |
ctx->tag_set = local_ts; |
} |
else |
THIS_TAG_DEBUG (sprintf ("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()); |
#endif |
Type ptype = parser->type; |
find_result_type: { |
|
|
foreach (tag->result_types, Type rtype) |
if (rtype->subtype_of (ptype)) { |
result_type = rtype; |
break find_result_type; |
} |
|
|
|
|
foreach (tag->result_types, Type rtype) |
if (ptype->subtype_of (rtype)) { |
result_type = ptype (rtype->_parser_prog); |
break find_result_type; |
} |
parse_error ( |
"Tag returns " + |
String.implode_nicely ([array(string)] tag->result_types->name, "or") + |
" but " + [string] parser->type->name + " is expected.\n"); |
} |
THIS_TAG_DEBUG (sprintf ("Resolved result_type to %O from surrounding %O\n", |
result_type, ptype)); |
} |
else THIS_TAG_DEBUG (sprintf ("Keeping result_type %O\n", result_type)); |
|
if (!content_type) { |
#ifdef MODULE_DEBUG |
if (!tag) fatal_error ("content_type not set in Frame object %O, " |
"and it has no Tag object to use for inferring it.\n", |
this_object()); |
#endif |
content_type = tag->content_type; |
if (content_type == t_same) { |
content_type = result_type (content_type->_parser_prog); |
THIS_TAG_DEBUG (sprintf ("Resolved t_same to content_type %O\n", |
content_type)); |
} |
else THIS_TAG_DEBUG (sprintf ("Setting content_type to %O from tag\n", |
content_type)); |
} |
else THIS_TAG_DEBUG (sprintf ("Keeping content_type %O\n", content_type)); |
|
if (raw_content) { |
THIS_TAG_DEBUG ("Initializing the content variable to nil\n"); |
content = content_type->empty_value; |
} |
|
mixed err = catch { |
switch (eval_state) { |
case EVSTAT_BEGIN: |
if (array|function(RequestID:array) do_enter = |
[array|function(RequestID:array)] this->do_enter) { |
if (!exec) { |
if (arrayp (do_enter)) { |
THIS_TAG_DEBUG ("Getting exec array from do_enter\n"); |
exec = [array] do_enter; |
} |
else { |
THIS_TAG_DEBUG ("Calling do_enter\n"); |
exec = ([function(RequestID:array)] do_enter) (id); |
THIS_TAG_DEBUG ((exec ? "Exec array" : "Zero") + |
" returned from do_enter\n"); |
} |
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
} |
|
if (exec) { |
if (!(flags & FLAG_PARENT_SCOPE)) { |
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this, "Entering scope\n"); |
ENTER_SCOPE (ctx, this); |
} |
mixed res = _exec_array (parser, exec, 0); |
|
if (flags & FLAG_STREAM_RESULT) { |
#ifdef DEBUG |
if (ctx->unwind_state) |
fatal_error ("Internal error: Clobbering unwind_state " |
"to do streaming.\n"); |
if (piece != nil) |
fatal_error ("Internal error: Thanks, we think about how nice " |
"it must be to play the harmonica...\n"); |
#endif |
if (result_type->name != parser->type->name) { |
THIS_TAG_DEBUG (sprintf ("Converting result from %s to %s of " |
"surrounding content\n", |
result_type->name, parser->type->name)); |
res = parser->type->encode (res, result_type); |
} |
ctx->unwind_state = (["stream_piece": res]); |
THIS_TAG_DEBUG (sprintf ("Streaming %t from do_enter\n", res)); |
throw (this); |
} |
|
exec = 0; |
} |
} |
eval_state = EVSTAT_ENTERED; |
|
|
case EVSTAT_ENTERED: |
case EVSTAT_LAST_ITER: |
do { |
if (eval_state != EVSTAT_LAST_ITER) { |
int|function(RequestID:int) do_iterate = |
[int|function(RequestID:int)] this->do_iterate; |
if (intp (do_iterate)) { |
iter = [int] do_iterate || 1; |
eval_state = EVSTAT_LAST_ITER; |
#ifdef DEBUG |
if (iter > 1) |
THIS_TAG_DEBUG (sprintf ("Getting %d iterations from do_iterate\n", |
iter)); |
else if (iter < 0) |
THIS_TAG_DEBUG ("Skipping to finish since do_iterate is negative\n"); |
#endif |
} |
else { |
THIS_TAG_DEBUG ("Calling do_iterate\n"); |
iter = ( do_iterate) ( |
id); |
THIS_TAG_DEBUG (sprintf ("%O returned from do_iterate\n", iter)); |
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
if (!iter) eval_state = EVSTAT_LAST_ITER; |
} |
} |
|
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this, "Entering scope\n"); |
ENTER_SCOPE (ctx, this); |
|
for (; iter > 0; |
iter-- |
#ifdef DEBUG |
, debug_iter++ |
#endif |
) { |
if (raw_content && raw_content != "") |
if (flags & FLAG_EMPTY_ELEMENT) |
parse_error ("This tag doesn't handle content.\n"); |
|
else { |
int finished = 0; |
if (!subparser) { |
if (this->local_tags) { |
subparser = content_type->get_parser ( |
ctx, [object(TagSet)] this->local_tags, parser); |
subparser->_local_tag_set = 1; |
THIS_TAG_DEBUG ( |
sprintf ("Iter[%d]: Evaluating content with %O " |
"from local_tags\n", debug_iter, subparser)); |
} |
else { |
subparser = content_type->get_parser (ctx, 0, parser); |
#ifdef DEBUG |
if (content_type->_parser_prog != PNone) |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Evaluating content " |
"with %O\n", debug_iter, subparser)); |
#endif |
} |
if (parser->recover_errors && !(flags & FLAG_DONT_RECOVER)) |
subparser->recover_errors = 1; |
subparser->finish (raw_content); |
finished = 1; |
} |
|
do { |
if (flags & FLAG_STREAM_CONTENT && subparser->read) { |
|
|
mixed res = subparser->read(); |
if (content_type->sequential) piece = res + piece; |
else if (piece == nil) piece = res; |
THIS_TAG_DEBUG ( |
sprintf ("Iter[%d]: Got %s %t stream piece\n", |
debug_iter, finished ? "ending" : "a", piece)); |
|
if (piece != nil) { |
array|function(RequestID,void|mixed:array) do_process; |
if ((do_process = |
[array|function(RequestID,void|mixed:array)] |
this->do_process) && |
!arrayp (do_process)) { |
if (!exec) { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Calling do_process in " |
"streaming mode\n", debug_iter)); |
exec = do_process (id, piece); |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: %s returned from " |
"do_process\n", debug_iter, |
exec ? "Exec array" : "Zero")); |
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
} |
|
if (exec) { |
THIS_TAG_DEBUG_ENTER_SCOPE ( |
ctx, this, sprintf ("Iter[%d]: Entering scope\n", |
debug_iter)); |
ENTER_SCOPE (ctx, this); |
mixed res = _exec_array ( |
parser, exec, flags & FLAG_PARENT_SCOPE); |
|
if (flags & FLAG_STREAM_RESULT) { |
#ifdef DEBUG |
if (!zero_type (ctx->unwind_state->stream_piece)) |
fatal_error ("Internal error: Clobbering " |
"unwind_state->stream_piece.\n"); |
#endif |
if (result_type->name != parser->type->name) { |
THIS_TAG_DEBUG ( |
sprintf ("Iter[%d]: Converting result from %s to %s" |
" of surrounding content\n", debug_iter, |
result_type->name, parser->type->name)); |
res = parser->type->encode (res, result_type); |
} |
ctx->unwind_state->stream_piece = res; |
THIS_TAG_DEBUG ( |
sprintf ("Iter[%d]: Streaming %t from " |
"do_process\n", debug_iter, res)); |
throw (this); |
} |
|
exec = 0; |
} |
else if (flags & FLAG_STREAM_RESULT) { |
THIS_TAG_DEBUG ( |
sprintf ("Iter[%d]: do_process finished the stream; " |
"ignoring remaining content\n", debug_iter)); |
ctx->unwind_state = 0; |
piece = nil; |
break; |
} |
} |
piece = nil; |
} |
|
if (finished) break; |
} |
else { |
piece = nil; |
if (finished) { |
mixed res = subparser->eval(); |
if (content_type->sequential) { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Adding %t to content\n", |
debug_iter, res)); |
content += res; |
} |
else if (res != nil) { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Setting content to %t\n", |
debug_iter, res)); |
content = res; |
} |
break; |
} |
} |
|
subparser->finish(); |
finished = 1; |
} while (1); |
subparser = 0; |
} |
|
if (array|function(RequestID,void|mixed:array) do_process = |
[array|function(RequestID,void|mixed:array)] this->do_process) { |
if (!exec) { |
if (arrayp (do_process)) { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Getting exec array from " |
"do_process\n", debug_iter)); |
exec = [array] do_process; |
} |
else { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Calling do_process\n", |
debug_iter)); |
exec = ([function(RequestID,void|mixed:array)] do_process) ( |
id); |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: %s returned from do_process\n", |
debug_iter, exec ? "Exec array" : "Zero")); |
} |
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
} |
|
if (exec) { |
THIS_TAG_DEBUG_ENTER_SCOPE ( |
ctx, this, sprintf ("Iter[%d]: Entering scope\n", debug_iter)); |
ENTER_SCOPE (ctx, this); |
mixed res = _exec_array ( |
parser, exec, flags & FLAG_PARENT_SCOPE); |
|
if (flags & FLAG_STREAM_RESULT) { |
#ifdef DEBUG |
if (ctx->unwind_state) |
fatal_error ("Internal error: Clobbering unwind_state " |
"to do streaming.\n"); |
if (piece != nil) |
fatal_error ("Internal error: Thanks, we think about how nice " |
"it must be to play the harmonica...\n"); |
#endif |
if (result_type->name != parser->type->name) { |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Converting result from " |
"type %s to type %s of surrounding " |
"content\n", debug_iter, |
result_type->name, parser->type->name)); |
res = parser->type->encode (res, result_type); |
} |
ctx->unwind_state = (["stream_piece": res]); |
THIS_TAG_DEBUG (sprintf ("Iter[%d]: Streaming %t from " |
"do_process\n", debug_iter, res)); |
throw (this); |
} |
|
exec = 0; |
} |
} |
|
} |
} while (eval_state != EVSTAT_LAST_ITER); |
|
|
case EVSTAT_ITER_DONE: |
if (array|function(RequestID:array) do_return = |
[array|function(RequestID:array)] this->do_return) { |
eval_state = EVSTAT_ITER_DONE; |
if (!exec) { |
if (arrayp (do_return)) { |
THIS_TAG_DEBUG ("Getting exec array from do_return\n"); |
exec = [array] do_return; |
} |
else { |
THIS_TAG_DEBUG ("Calling do_return\n"); |
exec = ([function(RequestID:array)] do_return) (id); |
THIS_TAG_DEBUG ((exec ? "Exec array" : "Zero") + |
" returned from do_return\n"); |
} |
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
} |
|
if (exec) { |
THIS_TAG_DEBUG_ENTER_SCOPE (ctx, this, "Entering scope\n"); |
ENTER_SCOPE (ctx, this); |
_exec_array (parser, exec, flags & FLAG_PARENT_SCOPE); |
exec = 0; |
} |
} |
|
else if (result == nil && !(flags & FLAG_EMPTY_ELEMENT)) { |
if (result_type->_parser_prog == PNone) { |
if (content_type->name != result_type->name) { |
THIS_TAG_DEBUG (sprintf ("Assigning content to result after " |
"converting from %s to %s\n", |
content_type->name, result_type->name)); |
result = result_type->encode (content, content_type); |
} |
else { |
THIS_TAG_DEBUG ("Assigning content to result\n"); |
result = content; |
} |
} |
else |
if (stringp (content)) { |
eval_state = EVSTAT_ITER_DONE; |
if (!exec) { |
THIS_TAG_DEBUG ("Parsing content with exec array " |
"for assignment to result\n"); |
exec = ({content}); |
} |
_exec_array (parser, exec, flags & FLAG_PARENT_SCOPE); |
exec = 0; |
} |
} |
else { |
#ifdef DEBUG |
if (!(flags & FLAG_EMPTY_ELEMENT)) |
THIS_TAG_DEBUG ("Skipping nil result\n"); |
#endif |
} |
|
THIS_TAG_DEBUG_LEAVE_SCOPE (ctx, this, "Leaving scope\n"); |
LEAVE_SCOPE (ctx, this); |
} |
|
if (ctx->new_runtime_tags) |
_handle_runtime_tags (ctx, parser); |
}; |
|
ctx->frame_depth--; |
|
if (err) { |
THIS_TAG_DEBUG_LEAVE_SCOPE (ctx, this, "Leaving scope\n"); |
LEAVE_SCOPE (ctx, this); |
string action; |
if (objectp (err) && ([object] err)->thrown_at_unwind) { |
mapping(string:mixed)|mapping(object:array) ustate = ctx->unwind_state; |
if (!ustate) ustate = ctx->unwind_state = ([]); |
#ifdef DEBUG |
if (ustate[this]) |
fatal_error ("Internal error: Frame already has an unwind state.\n"); |
#endif |
|
if (ustate->exec_left) { |
exec = [array] ustate->exec_left; |
m_delete (ustate, "exec_left"); |
} |
|
if (err == this || exec && sizeof (exec) && err == exec[0]) |
|
if (parser->unwind_safe) { |
|
|
|
if (err == this) err = 0; |
if (orig_tag_set) ctx->tag_set = orig_tag_set, orig_tag_set = 0; |
action = "break"; |
THIS_TAG_TOP_DEBUG ("Interrupted for streaming - " |
"breaking to parent frame\n"); |
} |
else { |
|
|
m_delete (ustate, "stream_piece"); |
action = "continue"; |
THIS_TAG_TOP_DEBUG ("Interrupted for streaming - " |
"continuing since parser isn't unwind safe\n"); |
} |
else if (!zero_type (ustate->stream_piece)) { |
|
|
piece = ustate->stream_piece; |
m_delete (ustate, "stream_piece"); |
action = "continue"; |
} |
else { |
action = "break"; |
THIS_TAG_TOP_DEBUG ("Interrupted\n"); |
} |
|
ustate[this] = ({err, eval_state, iter, raw_content, subparser, piece, |
exec, orig_tag_set, ctx->new_runtime_tags, |
#ifdef DEBUG |
debug_iter, |
#endif |
}); |
TRACE_LEAVE (action); |
} |
else { |
THIS_TAG_TOP_DEBUG ("Exception\n"); |
TRACE_LEAVE ("exception"); |
ctx->handle_exception (err, parser); |
result = nil; |
action = "return"; |
} |
|
switch (action) { |
case "break": |
#ifdef MODULE_DEBUG |
if (!parser->unwind_state) |
fatal_error ("Trying to unwind inside a parser that isn't unwind safe.\n"); |
#endif |
throw (this); |
case "continue": |
_eval (parser); |
return; |
case "return": |
break; |
default: |
fatal_error ("Internal error: Don't you come here and %O on me!\n", action); |
} |
} |
else { |
THIS_TAG_TOP_DEBUG ("Done\n"); |
TRACE_LEAVE (""); |
} |
} while (0); |
|
if (orig_tag_set) ctx->tag_set = orig_tag_set; |
ctx->frame = up; |
} |
|
MARK_OBJECT; |
|
string _sprintf() |
{ |
return "RXML.Frame(" + (tag && [string] tag->name) + ")" + OBJ_COUNT; |
} |
} |
|
|
|
|
|
|
final mixed get_var (string var, void|string scope_name, void|Type want_type) |
{return get_context()->get_var (var, scope_name, want_type);} |
final mixed user_get_var (string var, void|string scope_name, void|Type want_type) |
{return get_context()->user_get_var (var, scope_name, want_type);} |
final mixed set_var (string var, mixed val, void|string scope_name) |
{return get_context()->set_var (var, val, scope_name);} |
final mixed user_set_var (string var, mixed val, void|string scope_name) |
{return get_context()->user_set_var (var, val, scope_name);} |
final void delete_var (string var, void|string scope_name) |
{get_context()->delete_var (var, scope_name);} |
final void user_delete_var (string var, void|string scope_name) |
{get_context()->user_delete_var (var, scope_name);} |
|
final void run_error (string msg, mixed... args) |
|
|
|
|
{ |
if (sizeof (args)) msg = sprintf (msg, @args); |
array bt = backtrace(); |
throw (Backtrace ("run", msg, get_context(), bt[..sizeof (bt) - 2])); |
} |
|
final void parse_error (string msg, mixed... args) |
|
|
|
|
{ |
if (sizeof (args)) msg = sprintf (msg, @args); |
array bt = backtrace(); |
throw (Backtrace ("parse", msg, get_context(), bt[..sizeof (bt) - 2])); |
} |
|
final void fatal_error (string msg, mixed... args) |
|
|
|
{ |
if (sizeof (args)) msg = sprintf (msg, @args); |
array bt = backtrace(); |
throw_fatal (({msg, bt[..sizeof (bt) - 2]})); |
} |
|
final void throw_fatal (mixed err) |
|
|
{ |
if (arrayp (err) && sizeof (err) == 2 || |
objectp (err) && !err->is_RXML_Backtrace && err->is_generic_error) { |
string msg; |
if (catch (msg = err[0])) throw (err); |
if (stringp (msg) && !has_value (msg, "\nRXML frame backtrace:\n")) { |
string descr = Backtrace (0, 0)->describe_rxml_backtrace (1); |
if (sizeof (descr)) { |
if (sizeof (msg) && msg[-1] != '\n') msg += "\n"; |
msg += "RXML frame backtrace:\n" + descr; |
catch (err[0] = msg); |
} |
} |
} |
throw (err); |
} |
|
final mixed rxml_index (mixed val, string|int|array(string|int) index, |
string scope_name, Context ctx, void|Type want_type) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
if (arrayp (index) ? !sizeof (index) : !(stringp (index) || intp (index))) |
error ("Invalid index specifier.\n"); |
#endif |
|
int scope_got_type = 0; |
array(string|int) idxpath; |
if (arrayp (index)) idxpath = index, index = index[0]; |
else idxpath = ({0}); |
|
for (int i = 1;; i++) { |
|
if( arrayp( val ) ) |
if (intp (index) && index) |
if( (index > sizeof( val )) |
|| ((index < 0) && (-index > sizeof( val ) )) ) |
parse_error( "Index %d out of range for array of size %d in %s.\n", |
index, sizeof (val), scope_name ); |
else if( index < 0 ) |
val = val[index]; |
else |
val = val[index-1]; |
else |
parse_error( "Cannot index the array in %s with %O.\n", scope_name, index ); |
else if (val == nil) |
parse_error ("%s produced no value to index with %O.\n", scope_name, index); |
else if( objectp( val ) && val->`[] ) { |
#ifdef MODULE_DEBUG |
Scope scope = [object(Scope)] val; |
#endif |
if (zero_type ( |
val = ([object(Scope)] val)->`[]( |
index, ctx, scope_name, |
i == sizeof (idxpath) && (scope_got_type = 1, want_type)))) |
val = nil; |
#ifdef MODULE_DEBUG |
else if (mixed err = scope_got_type && want_type && |
!(objectp (val) && ([object] val)->rxml_var_eval) && |
catch (want_type->type_check (val))) |
if (([object] err)->is_RXML_Backtrace) |
error ("%O->`[] didn't return a value of the correct type:\n%s", |
scope, err->msg); |
else throw (err); |
#endif |
} |
else if( mappingp( val ) || objectp (val) ) { |
if (zero_type (val = val[ index ])) val = nil; |
} |
else if (multisetp (val)) { |
if (!val[index]) val = nil; |
} |
else if (!(<1, -1>)[index]) |
parse_error ("%s is %O which cannot be indexed with %O.\n", |
scope_name, val, index); |
|
if (i == sizeof (idxpath)) break; |
scope_name += "." + index; |
index = idxpath[i]; |
|
#ifdef MODULE_DEBUG |
mapping(object:int) called = ([]); |
#endif |
while (objectp (val) && ([object] val)->rxml_var_eval) { |
#ifdef MODULE_DEBUG |
|
|
|
|
|
if (called[val]) |
error ("Cyclic rxml_var_eval chain detected in %O.\n" |
"All called objects:%{ %O%}\n", val, indices (called)); |
called[val] = 1; |
#endif |
if (zero_type (val = ([object(Value)] val)->rxml_var_eval ( |
ctx, index, scope_name, 0))) { |
val = nil; |
break; |
} |
} |
} |
|
if (val == nil) |
return ([])[0]; |
else if (!objectp (val) || !([object] val)->rxml_var_eval) |
if (want_type && !scope_got_type) |
return |
|
zero_type (val = want_type->encode (val)) || val == nil ? ([])[0] : val; |
else |
return val; |
|
#ifdef MODULE_DEBUG |
mapping(object:int) called = ([]); |
#endif |
do { |
#ifdef MODULE_DEBUG |
if (called[val]) |
error ("Cyclic rxml_var_eval chain detected in %O.\n" |
"All called objects:%{ %O%}\n", val, indices (called)); |
called[val] = 1; |
Value val_obj = [object(Value)] val; |
#endif |
if (zero_type (val = ([object(Value)] val)->rxml_var_eval ( |
ctx, index, scope_name, want_type)) || |
val == nil) |
return ([])[0]; |
#ifdef MODULE_DEBUG |
else if (mixed err = want_type && catch (want_type->type_check (val))) |
if (([object] err)->is_RXML_Backtrace) |
error ("%O->rxml_var_eval didn't return a value of the correct type:\n%s", |
val_obj, err->msg); |
else throw (err); |
#endif |
} while (objectp (val) && ([object] val)->rxml_var_eval); |
return val; |
} |
|
final void tag_debug (string msg, mixed... args) |
|
|
{ |
if (Frame f = get_context()->frame) |
if (f->flags & FLAG_DEBUG) |
report_debug (msg, @args); |
} |
|
final Frame make_tag (string name, mapping(string:mixed) args, void|mixed content, |
void|Tag overridden_by) |
|
|
|
|
|
|
{ |
TagSet tag_set = get_context()->tag_set; |
Tag tag = overridden_by ? tag_set->get_overridden_tag (overridden_by) : |
tag_set->get_tag (name); |
return tag && tag (args, content); |
} |
|
final Frame make_unparsed_tag (string name, mapping(string:string) args, |
void|string content, void|Tag overridden_by) |
|
|
|
|
|
|
|
{ |
TagSet tag_set = get_context()->tag_set; |
Tag tag = overridden_by ? tag_set->get_overridden_tag (overridden_by) : |
tag_set->get_tag (name); |
if (!tag) return 0; |
Frame frame = tag (args, content); |
frame->flags |= FLAG_UNPARSED; |
return frame; |
} |
|
class parse_frame |
|
|
{ |
inherit Frame; |
constant flags = FLAG_UNPARSED; |
mapping(string:mixed) args = ([]); |
|
void create (Type type, string to_parse) |
{ |
content_type = type, result_type = type (PNone); |
content = to_parse; |
} |
|
string _sprintf() {return sprintf ("RXML.parse_frame(%O)", content_type);} |
} |
|
|
|
|
|
class Parser |
|
|
|
{ |
constant is_RXML_Parser = 1; |
constant thrown_at_unwind = 1; |
|
|
|
int error_count; |
|
|
|
function(Parser:void) data_callback; |
|
|
|
|
|
|
int write (string in) |
|
|
{ |
int 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 parser. " |
"Can't rewind.\n"); |
#endif |
m_delete (context->unwind_state, "top"); |
if (!sizeof (context->unwind_state)) context->unwind_state = 0; |
} |
if (feed (in)) res = 1; |
if (res && data_callback) data_callback (this_object()); |
}) |
if (objectp (err) && ([object] err)->thrown_at_unwind) { |
#ifdef DEBUG |
if (err != this_object()) { |
LEAVE_CONTEXT(); |
fatal_error ("Internal error: Unexpected unwind object catched.\n"); |
} |
#endif |
if (!context->unwind_state) context->unwind_state = ([]); |
context->unwind_state->top = err; |
} |
else { |
LEAVE_CONTEXT(); |
throw_fatal (err); |
} |
LEAVE_CONTEXT(); |
return res; |
} |
|
void write_end (void|string in) |
|
|
{ |
int 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 parser. " |
"Can't rewind.\n"); |
#endif |
m_delete (context->unwind_state, "top"); |
if (!sizeof (context->unwind_state)) context->unwind_state = 0; |
} |
finish (in); |
if (data_callback) data_callback (this_object()); |
}) |
if (objectp (err) && ([object] err)->thrown_at_unwind) { |
#ifdef DEBUG |
if (err != this_object()) { |
LEAVE_CONTEXT(); |
fatal_error ("Internal error: Unexpected unwind object catched.\n"); |
} |
#endif |
if (!context->unwind_state) context->unwind_state = ([]); |
context->unwind_state->top = err; |
} |
else { |
LEAVE_CONTEXT(); |
throw_fatal (err); |
} |
LEAVE_CONTEXT(); |
} |
|
array handle_var (string varref, Type surrounding_type) |
|
|
{ |
|
if(varref[0]==':') return ({type->format_entity (varref[1..])}); |
if (has_value (varref, ".")) |
if (mixed err = catch { |
|
|
|
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 ( |
"No scope in variable reference.\n" |
"(Use ':' in front to quote a character reference containing dots.)\n"); |
mixed val; |
if (zero_type (val = context->get_var ( |
splitted[1..], splitted[0], |
encoding ? t_string : surrounding_type))) { |
context->current_var = 0; |
return ({}); |
} |
context->current_var = 0; |
return encoding ? ({Roxen->roxen_encode (val, encoding)}) : ({val}); |
}) { |
context->current_var = 0; |
context->handle_exception (err, this_object()); |
return ({}); |
} |
if (!surrounding_type->free_text) |
parse_error ("Unknown variable reference &%s; not allowed in this " |
"context.\n", varref); |
return 0; |
} |
|
|
|
Context context; |
|
|
|
|
Type type; |
|
|
|
int compile; |
|
|
|
|
|
|
|
|
|
int recover_errors; |
|
|
|
mixed feed (string in); |
|
|
|
|
|
void finish (void|string in); |
|
|
|
|
optional int report_error (string msg); |
|
|
|
|
|
|
|
|
optional mixed read(); |
|
|
|
|
|
mixed eval(); |
|
|
|
|
|
optional PCode p_compile(); |
|
|
|
optional void reset (Context ctx, Type type, mixed... args); |
|
|
|
|
|
|
optional Parser clone (Context ctx, Type type, mixed... args); |
|
|
|
|
|
static void create (Context ctx, Type _type, mixed... args) |
{ |
context = ctx; |
type = _type; |
#ifdef RXML_OBJ_DEBUG |
__object_marker->create (this_object()); |
#endif |
} |
|
string current_input() {return 0;} |
|
|
|
|
|
Parser _next_free; |
|
|
Parser _parent; |
|
|
Stdio.File _source_file; |
mapping _defines; |
|
|
MARK_OBJECT_ONLY; |
|
string _sprintf() |
{ |
return sprintf ("RXML.Parser(%O)%s", type, OBJ_COUNT); |
} |
} |
|
|
class TagSetParser |
|
|
|
|
|
|
|
{ |
inherit Parser; |
|
constant is_RXML_TagSetParser = 1; |
constant tag_set_eval = 1; |
|
|
|
mixed eval() {return read();} |
|
|
|
TagSet tag_set; |
|
|
optional void reset (Context ctx, Type _type, TagSet tag_set, mixed... args); |
optional Parser clone (Context ctx, Type _type, TagSet tag_set, mixed... args); |
static void create (Context ctx, Type _type, TagSet _tag_set, mixed... args) |
{ |
|
context = ctx; |
type = _type; |
tag_set = _tag_set; |
#ifdef RXML_OBJ_DEBUG |
__object_marker->create (this_object()); |
#endif |
} |
|
|
|
mixed read(); |
|
|
|
|
optional void add_runtime_tag (Tag tag); |
|
|
|
|
optional void remove_runtime_tag (string|Tag tag, void|int proc_instr); |
|
|
|
|
|
|
|
|
|
int _local_tag_set; |
|
string _sprintf() |
{ |
return sprintf ("RXML.TagSetParser(%O,%O)%s", type, tag_set, OBJ_COUNT); |
} |
} |
|
class PNone |
|
{ |
inherit Parser; |
|
string data = ""; |
int evalpos = 0; |
|
int feed (string in) |
{ |
data += in; |
return 1; |
} |
|
void finish (void|string in) |
{ |
if (in) data += in; |
} |
|
string eval() |
{ |
string res = data[evalpos..]; |
evalpos = sizeof (data); |
return res; |
} |
|
string byte_compile() |
{ |
return data; |
} |
|
string byte_interpret (string byte_code, Context ctx) |
{ |
return byte_code; |
} |
|
void reset (Context ctx) |
{ |
context = ctx; |
data = ""; |
evalpos = 0; |
} |
|
string _sprintf() {return "RXML.PNone" + OBJ_COUNT;} |
} |
|
|
mixed simple_parse (string in, void|program parser) |
|
|
{ |
|
return t_any (parser || PExpr)->eval (in, Context (empty_tag_set)); |
} |
|
|
|
|
|
class Type |
|
|
|
|
|
|
|
|
|
{ |
constant is_RXML_Type = 1; |
|
|
|
int `== (mixed other) |
|
|
|
{ |
return |
objectp (other) && ([object] other)->is_RXML_Type && |
([object(Type)] other)->name == this_object()->name; |
} |
|
int subtype_of (Type other) |
|
|
|
|
{ |
return glob ([string] other->name, [string] this_object()->name); |
} |
|
int `< (mixed other) |
|
|
{ |
if (objectp (other) && ([object] other)->is_RXML_Type) { |
if (([object(Type)] other)->name == this_object()->name) return 0; |
return subtype_of (other); |
} |
return this_object()->name < other; |
} |
|
int convertible (Type from) |
|
|
{ |
if (conversion_type->name == from->name || |
conversion_type->name == from->conversion_type->name || |
this_object()->name == from->conversion_type->name || |
this_object()->name == from->name) |
return 1; |
|
|
for (Type tconv = conversion_type; tconv; tconv = tconv->conversion_type) |
for (Type fconv = from->conversion_type; fconv; fconv = fconv->conversion_type) |
if (fconv->name == tconv->name) |
return 1; |
return 0; |
} |
|
Type `() (program newparser, mixed... parser_args) |
|
|
|
{ |
Type newtype; |
if (sizeof (parser_args)) { |
newtype = clone(); |
newtype->_parser_prog = newparser; |
newtype->_parser_args = parser_args; |
if (newparser->tag_set_eval) newtype->_p_cache = set_weak_flag (([]), 1); |
} |
else { |
if (!_t_obj_cache) _t_obj_cache = ([]); |
if (!(newtype = _t_obj_cache[newparser])) |
if (newparser == _parser_prog) |
_t_obj_cache[newparser] = newtype = this_object(); |
else { |
_t_obj_cache[newparser] = newtype = clone(); |
newtype->_parser_prog = newparser; |
if (newparser->tag_set_eval) newtype->_p_cache = set_weak_flag (([]), 1); |
} |
} |
return newtype; |
} |
|
inline Parser get_parser (Context ctx, void|TagSet tag_set, void|Parser|PCode parent) |
|
{ |
Parser p; |
if (_p_cache) { |
TagSet tset = tag_set || ctx->tag_set; |
|
if (parent && parent->is_RXML_TagSetParser && |
tset == parent->tag_set && sizeof (ctx->runtime_tags) && |
parent->clone && parent->type == this_object()) { |
|
|
p = parent->clone (ctx, this_object(), tset, @_parser_args); |
p->_parent = parent; |
return p; |
} |
|
|
PCacheObj pco = _p_cache[tset]; |
if (pco && pco->tag_set_gen == tset->generation) { |
if ((p = pco->free_parser)) { |
pco->free_parser = p->_next_free; |
|
p->data_callback = p->compile = 0; |
p->reset (ctx, this_object(), tset, @_parser_args); |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
} |
|
else |
|
if (pco->clone_parser) |
p = pco->clone_parser->clone (ctx, this_object(), tset, @_parser_args); |
else if ((p = _parser_prog (ctx, this_object(), tset, @_parser_args))->clone) { |
|
|
p->context = 0; |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
p = (pco->clone_parser = p)->clone (ctx, this_object(), tset, @_parser_args); |
} |
} |
|
else { |
|
pco = PCacheObj(); |
pco->tag_set_gen = tset->generation; |
_p_cache[tset] = pco; |
if ((p = _parser_prog (ctx, this_object(), tset, @_parser_args))->clone) { |
|
|
p->context = 0; |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
p = (pco->clone_parser = p)->clone (ctx, this_object(), tset, @_parser_args); |
} |
} |
|
if (ctx->tag_set == tset && p->add_runtime_tag && sizeof (ctx->runtime_tags)) |
foreach (values (ctx->runtime_tags), Tag tag) |
p->add_runtime_tag (tag); |
} |
|
else { |
if ((p = free_parser)) { |
|
free_parser = p->_next_free; |
p->data_callback = p->compile = 0; |
p->reset (ctx, this_object(), @_parser_args); |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
} |
|
else if (clone_parser) |
|
p = clone_parser->clone (ctx, this_object(), @_parser_args); |
|
else if ((p = _parser_prog (ctx, this_object(), @_parser_args))->clone) { |
|
|
p->context = 0; |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
p = (clone_parser = p)->clone (ctx, this_object(), @_parser_args); |
} |
} |
|
p->_parent = parent; |
return p; |
} |
|
mixed eval (string in, void|Context ctx, void|TagSet tag_set, |
void|Parser|PCode parent, void|int dont_switch_ctx) |
|
|
|
{ |
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); |
else p->write_end (in); |
res = p->eval(); |
if (p->reset) { |
p->context = p->recover_errors = p->_parent = 0; |
#ifdef RXML_OBJ_DEBUG |
p->__object_marker->create (p); |
#endif |
if (_p_cache) { |
if (PCacheObj pco = _p_cache[tag_set || ctx->tag_set]) { |
|
p->_next_free = pco->free_parser; |
pco->free_parser = p; |
} |
} |
else { |
|
p->_next_free = free_parser; |
free_parser = p; |
} |
} |
} |
if (ctx->type_check) type_check (res); |
return res; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constant sequential = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
Type conversion_type; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void type_check (mixed val, void|string msg, mixed... args); |
|
|
|
|
|
|
|
|
mixed encode (mixed val, void|Type from); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
optional mixed decode (mixed val); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Type clone() |
|
|
|
{ |
Type newtype = object_program ((object(this_program)) this_object())(); |
newtype->_parser_prog = _parser_prog; |
newtype->_parser_args = _parser_args; |
newtype->_t_obj_cache = _t_obj_cache; |
return newtype; |
} |
|
string format_tag (string|Tag tag, void|mapping(string:string) args, |
void|string content, void|int flags) |
|
|
|
{ |
parse_error ("Cannot format tags with type %s.\n", this_object()->name); |
} |
|
string format_entity (string entity) |
|
|
{ |
parse_error ("Cannot format entities with type %s.\n", this_object()->name); |
} |
|
static final void type_check_error (string msg1, array args1, |
string msg2, mixed... args2) |
|
|
|
|
|
|
|
|
{ |
if (sizeof (args2)) msg2 = sprintf (msg2, @args2); |
if (msg1) { |
if (sizeof (args1)) msg1 = sprintf (msg1, @args1); |
parse_error (msg1 + ": " + msg2); |
} |
else parse_error (msg2); |
} |
|
final mixed indirect_convert (mixed val, Type from) |
|
|
|
|
|
{ |
if (conversion_type) { |
if (from->conversion_type) { |
string fromconvname = from->conversion_type->name; |
if (conversion_type->name == fromconvname) |
return encode (from->decode ? from->decode (val) : val, conversion_type); |
if (this_object()->name == fromconvname) |
return from->decode ? from->decode (val) : val; |
} |
string name = this_object()->name; |
if (name == from->name) |
return val; |
|
|
int levels = 1; |
for (Type conv = from->conversion_type; |
conv; |
conv = conv->conversion_type, levels++) |
if (conv->name == name) { |
while (levels--) { |
val = from->decode ? from->decode (val) : val; |
from = from->conversion_type; |
} |
return val; |
} |
if (conversion_type->conversion_type && |
conversion_type->conversion_type->name == from->name) |
|
return encode (conversion_type->encode (val, from), conversion_type); |
else { |
#ifdef MODULE_DEBUG |
if (conversion_type->name == from->name) |
error ("This function shouldn't be used to convert " |
"from the conversion type %s to %s; use encode() for that.\n", |
conversion_type->name, this_object()->name); |
#endif |
return encode (conversion_type->indirect_convert (val, from), conversion_type); |
} |
} |
parse_error ("Cannot convert type %s to %s.\n", from->name, this_object()->name); |
} |
|
|
|
program _parser_prog = PNone; |
|
|
array(mixed) _parser_args = ({}); |
|
mapping(program:Type) _t_obj_cache; |
|
|
|
private Parser clone_parser; |
private Parser free_parser; |
|
|
mapping(TagSet:PCacheObj) _p_cache; |
|
MARK_OBJECT_ONLY; |
|
string _sprintf() {return "RXML.Type(" + this_object()->name + ")" + OBJ_COUNT;} |
} |
|
static class PCacheObj |
{ |
int tag_set_gen; |
Parser clone_parser; |
Parser free_parser; |
} |
|
|
|
TAny t_any = TAny(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static class TAny |
{ |
inherit Type; |
constant name = "*"; |
constant conversion_type = 0; |
constant free_text = 1; |
|
mixed encode (mixed val, void|Type from) |
{ |
return val; |
} |
|
string _sprintf() {return "RXML.t_any" + OBJ_COUNT;} |
} |
|
TNil t_nil = TNil(); |
|
|
|
static class TNil |
{ |
inherit Type; |
constant name = "nil"; |
constant sequential = 1; |
Nil empty_value = nil; |
Type conversion_type = 0; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (val != nil) |
type_check_error (msg, args, "Expected nil, got %t.\n", val); |
} |
|
Nil encode (mixed val, void|Type from) |
{ |
if (from && from != local::name) |
val = indirect_convert (val, from); |
#ifdef MODULE_DEBUG |
type_check (val); |
#endif |
return nil; |
} |
|
int subtype_of (Type other) {return 1;} |
|
string _sprintf() {return "RXML.t_nil" + OBJ_COUNT;} |
} |
|
TSame t_same = TSame(); |
|
|
static class TSame |
{ |
inherit Type; |
constant name = "same"; |
string _sprintf() {return "RXML.t_same" + OBJ_COUNT;} |
} |
|
|
|
|
|
|
TScalar t_scalar = TScalar(); |
|
|
|
|
|
|
|
static class TScalar |
{ |
inherit Type; |
constant name = "scalar"; |
constant sequential = 0; |
Nil empty_value = nil; |
Type conversion_type = 0; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (!stringp (val) && !intp (val) && !floatp (val)) |
type_check_error (msg, args, "Expected scalar value, got %t.\n", val); |
} |
|
string|int|float encode (mixed val, void|Type from) |
{ |
if (from) |
switch (from->name) { |
case TAny.name: type_check (val); |
case local::name: return [string|int|float] val; |
default: return [string|int|float] indirect_convert (val, from); |
} |
if (!stringp (val) && !intp (val) && !floatp (val)) |
|
parse_error ("Cannot convert value of type %t to scalar.\n", val); |
return [string|int|float] val; |
} |
|
string _sprintf() {return "RXML.t_scalar" + OBJ_COUNT;} |
} |
|
TNum t_num = TNum(); |
|
|
|
|
|
|
static class TNum |
{ |
inherit Type; |
constant name = "number"; |
constant sequential = 0; |
constant empty_value = 0; |
Type conversion_type = t_scalar; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (!intp (val) && !floatp (val)) |
type_check_error (msg, args, "Expected numeric value, got %t.\n", val); |
} |
|
int|float encode (mixed val, void|Type from) |
{ |
if (from) |
switch (from->name) { |
case TAny.name: type_check (val); |
case local::name: return [int|float] val; |
default: return [int|float] indirect_convert (val, from); |
case TScalar.name: |
} |
if (stringp (val)) |
if (sscanf (val, "%d%*c", int i) == 1) return i; |
else if (sscanf (val, "%f%*c", float f) == 1) return f; |
else parse_error ("String contains neither integer nor float.\n"); |
if (!intp (val) && !floatp (val)) |
|
parse_error ("Cannot convert value of type %t to number.\n", val); |
return [int|float] val; |
} |
|
string _sprintf() {return "RXML.t_num" + OBJ_COUNT;} |
} |
|
TInt t_int = TInt(); |
|
|
static class TInt |
{ |
inherit Type; |
constant name = "int"; |
constant sequential = 0; |
constant empty_value = 0; |
Type conversion_type = t_scalar; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (!intp (val)) |
type_check_error (msg, args, "Expected integer value, got %t.\n", val); |
} |
|
int encode (mixed val, void|Type from) |
{ |
if (from) |
switch (from->name) { |
case TAny.name: type_check (val); |
case local::name: return [int] val; |
default: return [int] indirect_convert (val, from); |
case TScalar.name: |
} |
if (stringp (val)) |
if (sscanf (val, "%d%*c", int i) == 1) return i; |
else parse_error ("String does not contain an integer.\n"); |
mixed err = catch {return (int) val;}; |
parse_error ("Cannot convert value to integer: " + describe_error (err)); |
} |
|
string _sprintf() {return "RXML.t_int" + OBJ_COUNT;} |
} |
|
TFloat t_float = TFloat(); |
|
|
static class TFloat |
{ |
inherit Type; |
constant name = "float"; |
constant sequential = 0; |
constant empty_value = 0; |
Type conversion_type = t_scalar; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (!floatp (val)) |
type_check_error (msg, args, "Expected float value, got %t.\n", val); |
} |
|
float encode (mixed val, void|Type from) |
{ |
if (from) |
switch (from->name) { |
case TAny.name: type_check (val); |
case local::name: return [float] val; |
default: return [float] indirect_convert (val, from); |
case TScalar.name: |
} |
if (stringp (val)) |
if (sscanf (val, "%f%*c", float f) == 1) return f; |
else parse_error ("String does not contain a float.\n"); |
mixed err = catch {return (float) val;}; |
parse_error ("Cannot convert value to float: " + describe_error (err)); |
} |
|
string _sprintf() {return "RXML.t_float" + OBJ_COUNT;} |
} |
|
|
|
TString t_string = TString(); |
|
|
|
|
|
|
|
|
|
|
static class TString |
{ |
inherit Type; |
constant name = "text/*"; |
constant sequential = 1; |
constant empty_value = ""; |
Type conversion_type = t_scalar; |
constant free_text = 1; |
|
void type_check (mixed val, void|string msg, mixed... args) |
{ |
if (!stringp (val)) |
type_check_error (msg, args, "Expected string for %s, got %t.\n", name, val); |
} |
|
string encode (mixed val, void|Type from) |
{ |
if (from) |
switch (from->name) { |
case TAny.name: type_check (val); |
case local::name: return [string] val; |
default: |
if (from->subtype_of (this_object())) { |
#ifdef MODULE_DEBUG |
type_check (val); |
#endif |
return [string] val; |
} |
return [string] indirect_convert (val, from); |
|