ed8175 | 1999-12-11 | Martin Stjernholm | |
#pragma strict_types
#if !constant (RequestID)
class RequestID {}
#endif
class Tag
{
int flags;
mapping(string:Type) req_arg_types;
mapping(string:Type) opt_arg_types;
Type content_type = t_text (PHtml);
array(Type) result_types = ({t_text});
string scope_name;
TagSet additional_tags, local_tags;
inline Frame `() (mapping(string:mixed) args, void|mixed|PCode content)
{
Tag this = [object(Tag)] this_object();
Frame frame = [object(Frame)] this->frame();
frame->tag = this;
frame->flags = flags;
if (scope_name) frame->scope_name = scope_name;
if (additional_tags) frame->additional_tags = additional_tags;
if (local_tags) frame->local_tags = local_tags;
frame->args = args;
if (!zero_type (content)) frame->content = content;
return frame;
}
array handle_tag (TagSetParser parser, mapping(string:string) args, void|string content)
{
Frame frame = `() (args, Void);
frame->_eval (parser, args, content);
return frame->result == Void ? ({}) : ({frame->result});
}
string _sprintf()
{
return "Tag(" + [string] this_object()->name + ")";
}
}
class TagSet
{
string prefix;
int prefix_required;
array(TagSet) imported = ({});
int generation = 1;
mapping(string:mixed) low_tags, low_containers, low_entities;
void create (void|array(Tag) _tags)
{
if (_tags) tags = mkmapping ([array(string)] _tags->name, _tags);
}
void add_tag (Tag tag)
{
tags[tag->name] = tag;
changed();
}
void add_tags (array(Tag) _tags)
{
tags += mkmapping ( _tags->name, _tags);
changed();
}
void remove_tag (string|Tag tag)
{
if (stringp (tag))
m_delete (tags, tag);
else for (string n; !zero_type (n = search (tags, tag));)
m_delete (tags, n);
changed();
}
Tag get_tag (string name)
{
Tag tag;
if ((tag = tags[name])) return tag;
foreach (imported, TagSet tag_set)
if ((tag = [object(Tag)] tag_set->get_tag (name))) return tag;
return 0;
}
Tag get_local_tag (string name)
{
return tags[name];
}
array(Tag) get_local_tags()
{
return values (tags);
}
mixed `->= (string var, mixed val)
{
switch (var) {
case "imported":
(imported - ({0}))->dont_notify (changed);
imported = [array(TagSet)] val;
imported->do_notify (changed);
break;
default:
::`->= (var, val);
}
changed();
return val;
}
mixed `[]= (string var, mixed val) {return `->= (var, val);}
Parser `() (Type top_level_type, void|RequestID id)
{
return Context (this_object(), id)->new_parser (top_level_type);
}
void changed()
{
generation++;
(notify_funcs -= ({0}))();
set_weak_flag (notify_funcs, 1);
}
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());
}
private mapping(string:Tag) tags = ([]);
private array(function(:void)) notify_funcs = ({});
}
TagSet empty_tag_set;
class Context
{
Frame frame;
RequestID id;
int type_check;
TagSet tag_set;
int tag_set_is_local;
mixed get_var (string var, void|string scope_name)
{
if (mapping(string:mixed) vars = scopes[scope_name || ""]) {
mixed val;
if (zero_type (val = vars[var])) return ([])[0];
else if (objectp (val) && val->eval)
return val->eval (this_object(), var, scope_name);
else return val;
}
else if (scope_name) error ("Unknown scope %O.\n", scope_name);
else error ("No current scope.\n");
}
mixed set_var (string var, mixed val, void|string scope_name)
{
if (mapping(string:mixed) vars = scopes[scope_name || ""])
return vars[var] = val;
else if (scope_name) error ("Unknown scope %O.\n", scope_name);
else error ("No current scope.\n");
}
void delete_var (string var, void|string scope_name)
{
if (mapping(string:mixed) vars = scopes[scope_name || ""])
m_delete (vars, var);
else if (scope_name) error ("Unknown scope %O.\n", scope_name);
else error ("No current scope.\n");
}
array(string) list_var (void|string scope_name)
{
if (mapping(string:mixed) vars = scopes[scope_name || ""])
return indices (vars);
else if (scope_name) error ("Unknown scope %O.\n", scope_name);
else error ("No current scope.\n");
}
void add_runtime_tag (Tag tag)
{
if (tag_set_is_local) make_tag_set_local();
tag_set->add_tag (tag);
}
void remove_runtime_tag (string|Tag tag)
{
if (tag_set_is_local) make_tag_set_local();
tag_set->remove_tag (tag);
}
array(string) list_scopes()
{
return indices (scopes) - ({""});
}
void add_scope (string scope_name, mapping(string:mixed) vars)
{
if (scopes[scope_name])
if (scope_name == "") {
mapping(string:mixed) inner = scopes[""];
while (mapping(string:mixed) outer = hidden[inner]) inner = outer;
hidden[inner] = vars;
}
else {
Frame outermost;
for (Frame f = frame; f; f = f->up)
if (f->scope_name == scope_name) outermost = f;
if (outermost) hidden[outermost] = vars;
else scopes[scope_name] = vars;
}
else scopes[scope_name] = vars;
}
void remove_scope (string scope_name)
{
#ifdef MODULE_DEBUG
if (scope_name == "") 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 (mapping(string:mixed) vars = scopes[""]) {
string scope_name;
while (scope_name = search (scopes, vars, scope_name))
if (scope_name != "") return scope_name;
}
return 0;
}
void error (string msg, mixed... args)
{
if (sizeof (args)) msg = sprintf (msg, @args);
msg = "RXML parser error: " + msg;
for (Frame f = frame; f; f = f->up) {
if (f->tag) msg += "<" + f->tag->name;
else if (!f->up) break;
else msg += "<(unknown tag)";
if (f->args)
foreach (sort (indices (f->args)), string arg) {
mixed val = f->args[arg];
msg += " " + arg + "=";
if (arrayp (val)) msg += map (val, error_print_val) * ",";
else msg += error_print_val (val);
}
else msg += " (no argmap)";
msg += ">\n";
}
array b = backtrace();
throw (({msg, b[..sizeof (b) - 2]}));
}
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);
}
mapping(string:mapping(string:mixed)) scopes = ([]);
mapping(mapping(string:mixed)|Frame:mapping(string:mixed)) hidden = ([]);
void enter_scope (Frame frame)
{
mapping(string:mixed) vars;
#ifdef DEBUG
if (!frame->vars) error ("Internal error: Frame has no variables.\n");
#endif
if ((vars = [mapping(string:mixed)] frame->vars) != scopes[""]) {
hidden[vars] = scopes[""];
scopes[""] = vars;
if (string scope_name = [string] frame->scope_name) {
hidden[frame] = scopes[scope_name];
scopes[scope_name] = vars;
}
}
}
void leave_scope (Frame frame)
{
if (string scope_name = [string] frame->scope_name)
if (hidden[frame]) {
scopes[scope_name] = hidden[frame];
m_delete (hidden, frame);
}
mapping(string:mixed) vars;
if (hidden[vars = [mapping(string:mixed)] frame->vars]) {
scopes[""] = hidden[vars];
m_delete (hidden, vars);
}
}
#define ENTER_SCOPE(ctx, frame) (frame->vars && ctx->enter_scope (frame))
#define LEAVE_SCOPE(ctx, frame) (frame->vars && ctx->leave_scope (frame))
void make_tag_set_local()
{
if (!tag_set_is_local) {
TagSet new_tag_set = TagSet();
new_tag_set->imported = ({tag_set});
tag_set = new_tag_set;
tag_set_is_local = 1;
}
}
Parser new_parser (Type top_level_type)
{
#ifdef MODULE_DEBUG
if (in_use || frame) error ("Context already in use.\n");
#endif
return top_level_type->get_parser (this_object());
}
void create (TagSet _tag_set, void|RequestID _id)
{
tag_set = _tag_set;
id = _id;
}
mapping(string:mixed)|mapping(Frame:array) unwind_state;
#ifdef MODULE_DEBUG
int in_use;
#endif
}
#if constant (thread_create)
private object _context = thread_local();
inline void set_context (Context ctx) {_context->set (ctx);}
inline Context get_context() {return [object(Context)] _context->get();}
#else
private Context _context;
inline void set_context (Context ctx) {_context = ctx;}
inline Context get_context() {return _context;}
#endif
#ifdef MODULE_DEBUG
#define ENTER_CONTEXT(ctx) \
Context __old_ctx = get_context(); \
set_context (ctx); \
if (ctx) { \
if (ctx->in_use && __old_ctx != ctx) \
parse_error ("Attempt to use context asynchronously.\n"); \
ctx->in_use = 1; \
}
#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
void parse_error (string msg, mixed... args)
{
Context ctx = get_context();
if (ctx && ctx->error)
ctx->error (msg, @args);
else {
if (sizeof (args)) msg = sprintf (msg, @args);
msg = "RXML parser error (no context): " + msg;
array b = backtrace();
throw (({msg, b[..sizeof (b) - 2]}));
}
}
constant FLAG_CONTAINER = 0x00000001;
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_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_SAME_STACK = 0x00100000;
constant FLAG_CACHE_EXECUTE_RESULT = 0x00200000;
class Frame
{
constant is_RXML_Frame = 1;
Frame up;
Tag tag;
int flags;
mapping(string:mixed) args;
Type content_type;
mixed content = Void;
Type result_type;
mixed result = Void;
void error (string msg, mixed... args)
{
parse_error (msg, @args);
}
void terminate()
{
}
void suspend()
{
}
void resume()
{
}
mixed _exec_array (Context ctx, array exec)
{
Frame this = [object(Frame)] this_object();
int i = 0;
mixed res = Void;
Parser subparser = 0;
mixed err = catch {
if (flags & FLAG_PARENT_SCOPE) LEAVE_SCOPE (ctx, this);
for (; i < sizeof (exec); i++) {
mixed elem = exec[i], piece = Void;
switch (sprintf ("%t", elem)) {
case "string":
if (result_type->_parser_prog == PNone)
piece = elem;
else {
subparser = result_type->get_parser (ctx);
subparser->finish (elem);
piece = subparser->eval();
subparser = 0;
}
break;
case "object":
if (elem->is_RXML_Frame) {
elem->_eval (0);
piece = elem->result;
}
else if (elem->is_RXML_Parser) {
elem->finish();
piece = elem->eval();
}
else
error ("File objects not yet implemented.\n");
break;
case "mapping":
error ("Header mappings not yet implemented.\n");
break;
case "multiset":
if (sizeof (elem) == 1) piece = ((array) elem)[0];
else if (sizeof (elem) > 1)
error (sizeof (elem) + " values in multiset in exec array.\n");
else error ("No value in multiset in exec array.\n");
break;
default:
error ("Invalid type %t in exec array.\n", elem);
}
if (result_type->sequential) res += piece;
else if (piece != Void) result = res = piece;
}
if (result_type->sequential) result += res;
if (flags & FLAG_PARENT_SCOPE) ENTER_SCOPE (ctx, this);
return res;
};
if (result_type->sequential) result += res;
if (objectp (err) && err->is_RXML_Frame) {
mapping(string:mixed)|mapping(Frame:array) ustate;
if ((ustate = ctx->unwind_state) && !zero_type (ustate->stream_piece))
if (result_type->sequential)
ustate->stream_piece = res + ustate->stream_piece;
else if (ustate->stream_piece == Void)
ustate->stream_piece = res;
ustate->exec_left = exec[i..];
if (subparser)
ustate->exec_left[0] = subparser;
}
throw (err);
}
void _eval (TagSetParser parser,
void|mapping(string:string) raw_args,
void|string raw_content)
{
Frame this = [object(Frame)] this_object();
Context ctx = parser->context;
#ifdef DEBUG
if (ctx != get_context()) error ("Internal error: Context not current.\n");
if (!parser->tag_set_eval)
error ("Internal error: Calling _eval() with non-tag set parser.\n");
#endif
int|function(RequestID:int|function) fn, iter;
Parser subparser;
mixed piece;
Frame subframe;
array exec;
int tags_added;
#define PRE_INIT_ERROR(X) (ctx->frame = this, error (X))
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
[subframe, fn, iter, raw_content, subparser, piece, exec, tags_added] = state;
m_delete (ctx->unwind_state, this);
if (!sizeof (ctx->unwind_state)) ctx->unwind_state = 0;
#ifdef DEBUG
if (piece && subframe && exec && (!sizeof (exec) || exec[0] != subframe))
PRE_INIT_ERROR ("Internal error: Subframe ambiguity "
"when handling a stream piece.\n");
#endif
#ifdef MODULE_DEBUG
if (piece && !(flags & FLAG_STREAM_CONTENT))
PRE_INIT_ERROR ("The subframe failed to notice that this frame doesn't support "
"streaming - flags did probably change.\n");
#endif
}
else {
#ifdef MODULE_DEBUG
if (up && up != ctx->frame)
PRE_INIT_ERROR ("Reuse of frame in different context.\n");
#endif
up = ctx->frame;
piece = Void;
}
#undef PRE_INIT_ERROR
ctx->frame = this;
int tag_set_gen = [int] parser->tag_set->generation;
if (raw_args) {
args = ([]);
mapping(string:Type) atypes;
if (tag->req_arg_types) {
atypes = [mapping(string:Type)] (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");
}
}
if (tag->opt_arg_types)
if (atypes)
atypes += (raw_args & tag->opt_arg_types);
else
atypes = [mapping(string:Type)] (raw_args & tag->opt_arg_types);
if (atypes)
if (mixed err = catch {
foreach (indices (atypes), string arg)
args[arg] =
atypes[arg]->eval (raw_args[arg], ctx, 0, 1);
}) {
if (objectp (err) && err->is_RXML_Frame)
error ("Can't save parser state when evaluating arguments.\n");
throw (err);
}
}
#ifdef DEBUG
if (!args) error ("Internal error: args not set.\n");
#endif
if (TagSet add_tags = raw_content && [object(TagSet)] this->additional_tags) {
if (!ctx->tag_set_is_local) ctx->make_tag_set_local();
if (search (ctx->tag_set->imported, add_tags) < 0) {
ctx->tag_set->imported = ({add_tags}) + ctx->tag_set->imported;
tags_added = 1;
}
}
if (!result_type) {
Type ptype = [object(Type)] parser->type;
foreach (tag->result_types, Type rtype)
if (rtype->subtype_of (ptype)) {result_type = rtype; break;}
if (!result_type)
error ("Tag returns " + String.implode_nicely (tag->result_types->name, "or") +
" but " + parser->type->name + " is expected.\n");
}
if (!content_type) content_type = tag->content_type || result_type;
mixed err = catch {
if (!fn)
fn = this->do_enter ?
[int|function(RequestID:int|function)] this->do_enter (ctx->id) :
1;
do {
if (!iter) {
iter = fn;
while (functionp (iter)) {
int|function(RequestID:int|function) newiter =
[int|function(mixed:int|function)] iter (ctx->id);
fn = iter, iter = newiter;
}
}
ENTER_SCOPE (ctx, this);
for (; iter > 0; iter--) {
if (raw_content) {
int finished = 0;
if (!subparser) {
subparser = content_type->get_parser (ctx, this->local_tags);
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 == Void) piece = res;
if (piece != Void) {
array|function(RequestID,mixed:array) do_return;
if ((do_return =
[array|function(RequestID,mixed:array)] this->do_return) &&
!arrayp (do_return)) {
if (!exec) exec = do_return (ctx->id, piece);
if (exec) {
mixed res = _exec_array (ctx, exec);
if (flags & FLAG_STREAM_RESULT) {
#ifdef DEBUG
if (!zero_type (ctx->unwind_state->stream_piece))
error ("Internal error: "
"Clobbering unwind_state->stream_piece.\n");
#endif
ctx->unwind_state->stream_piece = res;
throw (this);
}
exec = 0;
}
else if (flags & FLAG_STREAM_RESULT) {
ctx->unwind_state = 0;
piece = Void;
break;
}
}
piece = Void;
}
if (finished) break;
}
else {
piece = Void;
if (finished) {
mixed res = subparser->eval();
if (content_type->sequential) content += res;
else if (res != Void) content = res;
break;
}
}
if (subframe && subparser->tag_set_eval) {
subframe->_eval (subparser);
if (subframe->result != Void)
subparser->write_out (subframe->result);
subframe = 0;
}
subparser->finish();
finished = 1;
} while (1);
subparser = 0;
}
if (array|function(RequestID,mixed:array) do_return =
[array|function(RequestID,mixed:array)] this->do_return) {
if (!exec)
exec = arrayp (do_return) ?
[array] do_return : do_return (ctx->id);
if (exec) {
mixed res = _exec_array (ctx, exec);
if (flags & FLAG_STREAM_RESULT) {
#ifdef DEBUG
if (ctx->unwind_state)
error ("Internal error: Clobbering unwind_state to do streaming.\n");
if (piece != Void)
error ("Internal error: Thanks, we think about how nice it must "
"be to play the harmonica...\n");
#endif
ctx->unwind_state = (["stream_piece": res]);
throw (this);
}
}
}
else if (result == Void && content_type->subtype_of (result_type))
result = content;
}
} while (fn);
};
LEAVE_SCOPE (ctx, this);
if (tag_set_gen != parser->tag_set->generation &&
ctx->tag_set == parser->tag_set)
parser->recheck_tags();
if (err) {
string action;
if (objectp (err) && err->is_RXML_Frame) {
mapping(string:mixed)|mapping(Frame:array) ustate = ctx->unwind_state;
if (!ustate) ustate = ctx->unwind_state = ([]);
#ifdef DEBUG
if (ustate[this])
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 (err == this) err = 0;
if (tags_added) {
ctx->tag_set->imported -= ({ this->additional_tags});
tags_added = 0;
}
action = "break";
}
else if (!zero_type (ustate->stream_piece)) {
piece = ustate->stream_piece;
m_delete (ustate, "stream_piece");
action = "continue";
}
else action = "break";
ustate[this] = ({err, fn, iter, raw_content, subparser, piece, exec, tags_added});
}
else action = "throw";
switch (action) {
case "break":
throw (this);
case "continue":
_eval (parser);
return;
case "throw":
throw (err);
default:
error ("Internal error: Don't you come here and %O on me!\n", action);
}
}
else {
if (tags_added)
ctx->tag_set->imported -= ({ this->additional_tags});
ctx->frame = up;
}
}
string _sprintf()
{
return "Frame(" + (tag && [string] tag->name) + ")";
}
}
class Parser
{
constant is_RXML_Parser = 1;
function(Parser:void) data_callback;
int write (string in)
{
int res;
ENTER_CONTEXT (context);
mixed err = catch {
if (context && context->unwind_state) _handle_rewind();
if (feed (in)) res = 1;
if (res && data_callback) data_callback (this_object());
};
LEAVE_CONTEXT();
if (err) _handle_unwind (err);
return res;
}
void write_end (void|string in)
{
int res;
ENTER_CONTEXT (context);
mixed err = catch {
if (context && context->unwind_state) _handle_rewind();
finish (in);
if (data_callback) data_callback (this_object());
};
LEAVE_CONTEXT();
if (err) _handle_unwind (err);
}
Context context;
Type type;
int compile;
mixed feed (string in);
void finish (void|string in);
void create (Context ctx, Type _type )
{
context = ctx;
type = _type;
}
Parser _next_free;
void _handle_rewind()
{
Parser this = [object(Parser)] this_object();
mapping(string:mixed)|mapping(Frame:array) ustate;
if ((ustate = context->unwind_state) && ustate->top && this->write_out) {
#ifdef MODULE_DEBUG
if (ustate->top[1] != this)
context->error ("Resuming parse state with different parser.\n");
#endif
Frame top = [object(Frame)] ustate->top[0];
m_delete (ustate, "top");
if (!sizeof (ustate)) context->unwind_state = ustate = 0;
top->_eval (this);
if (top->result != Void) this->write_out (top->result);
}
}
void _handle_unwind (mixed err)
{
if (context && objectp (err) && err->is_RXML_Frame) {
mapping(string:mixed)|mapping(Frame:array) ustate = context->unwind_state;
if (!ustate) ustate = context->unwind_state = ([]);
#ifdef DEBUG
if (ustate->exec_left || ustate->stream_piece || ustate->top)
error ("Internal error: Unexpected unwind_state at top level: %O\n", ustate);
#endif
ustate->top = ({err, this_object()});
}
else throw (err);
}
}
class TagSetParser
{
inherit Parser;
constant tag_set_eval = 1;
TagSet tag_set;
void write_out (mixed data);
mixed read();
void create (Context ctx, Type type, TagSet _tag_set )
{
::create (ctx, type);
tag_set = _tag_set;
}
void recheck_tags();
mixed eval()
{
return read();
}
}
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;
}
}
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;
|
ed8175 | 1999-12-11 | Martin Stjernholm | |
void type_check (mixed val);
Type clone()
{
Type newtype =
[object(Type)] object_program ([object(Type)] this_object())();
newtype->_parser_prog = _parser_prog;
newtype->_parser_args = _parser_args;
newtype->_t_obj_cache = _t_obj_cache;
return newtype;
}
int `== (mixed other)
{
return objectp (other) && other->is_RXML_Type &&
other->name == this_object()->name;
}
int subtype_of (Type other)
{
return glob ([string] other->name,
[string] ([object(Type)] this_object())->name);
}
Type `() (program newparser, mixed... parser_args)
{
Type newtype;
if (sizeof (parser_args)) {
newtype = clone();
newtype->_parser_args = parser_args;
if (newparser->tag_set_eval) newtype->_p_cache = ([]);
}
else {
if (!_t_obj_cache) _t_obj_cache = ([]);
if (!(newtype = _t_obj_cache[newparser]))
if (newparser == _parser_prog)
_t_obj_cache[newparser] = newtype = [object(Type)] this_object();
else {
_t_obj_cache[newparser] = newtype = clone();
newtype->_parser_prog = newparser;
if (newparser->tag_set_eval) newtype->_p_cache = ([]);
}
}
return newtype;
}
inline Parser get_parser (Context ctx, void|TagSet tag_set)
{
Parser p;
if (_p_cache) {
TagSet tset;
PCacheObj pco = _p_cache[tset = tag_set || ctx->tag_set];
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(), @_parser_args);
}
else
if (pco->clone_parser)
p = [object(Parser)] pco->clone_parser->clone (
ctx, this_object(), @_parser_args);
else if ((p = [object(Parser)] _parser_prog (
ctx, this_object(), @_parser_args))->clone)
p = [object(Parser)] (pco->clone_parser = p)->clone (
ctx, this_object(), @_parser_args);
}
else {
pco = PCacheObj();
pco->tag_set_gen = [int] tset->generation;
_p_cache[tset] = pco;
if ((p = [object(Parser)] _parser_prog (
ctx, this_object(), @_parser_args))->clone)
p = [object(Parser)] (pco->clone_parser = p)->clone (
ctx, this_object(), @_parser_args);
}
}
else {
if ((p = free_parser)) {
free_parser = p->_next_free;
p->data_callback = p->compile = 0;
p->reset (ctx, this_object(), @_parser_args);
}
else if (clone_parser)
p = [object(Parser)] clone_parser->clone (
ctx, this_object(), @_parser_args);
else if ((p = [object(Parser)] _parser_prog (
ctx, this_object(), @_parser_args))->clone)
p = [object(Parser)] (clone_parser = p)->clone (
ctx, this_object(), @_parser_args);
}
return p;
}
mixed eval (string in, void|Context ctx, void|TagSet tag_set, 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);
if (dont_switch_ctx) p->finish (in);
else p->write_end (in);
res = p->eval();
if (p->reset)
if (_p_cache) {
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;
}
program _parser_prog = PNone;
private array(mixed) _parser_args = ({});
mapping(program:Type) _t_obj_cache;
private Parser clone_parser;
private Parser free_parser;
private class PCacheObj
{
int tag_set_gen;
Parser clone_parser;
Parser free_parser;
}
mapping(TagSet:PCacheObj) _p_cache;
}
Type t_text = class
{
inherit Type;
constant name = "text/*";
constant sequential = 1;
constant empty_value = "";
constant free_text = 1;
}();
Type t_any = class
{
inherit Type;
constant name = "*";
}();
class VarRef
{
constant is_RXML_VarRef = 1;
string scope, var;
void create (string _scope, string _var) {scope = _scope, var = _var;}
int valid (Context ctx) {return !!ctx->scopes[scope];}
mixed get (Context ctx) {return ctx->scopes[scope][var];}
mixed set (Context ctx, mixed val) {return ctx->scopes[scope][var] = val;}
void remove (Context ctx) {m_delete (ctx->scopes[scope], var);}
string name() {return scope + "." + var;}
}
class PCode
{
constant is_RXML_PCode = 1;
array p_code = ({});
mixed eval (Context ctx)
{
}
}
static class VoidType
{
mixed `+ (mixed... vals) {return sizeof (vals) ? predef::`+ (@vals) : this_object();}
mixed ``+ (mixed val) {return val;}
int `!() {return 1;}
string _sprintf (string flag) {return flag == "O" && "Void";}
};
VoidType Void = VoidType();
class ScanStream
{
private class Link
{
array data;
Link next;
}
private Link head = Link();
private Link tail = head;
private int next_token = 0;
private string end = "";
private int fin = 0;
array scan (string in, int finished);
void feed (string in)
{
#ifdef MODULE_DEBUG
if (fin) error ("Cannot feed data to a finished stream.\n");
#endif
array tokens = scan (end + in, 0);
end = [string] tokens[-1];
if (sizeof (tokens) > 1) {
tail->data = tokens[..sizeof (tokens) - 2];
tail = tail->next = Link();
}
}
void finish (void|string in)
{
if (in || !fin && sizeof (end)) {
#ifdef MODULE_DEBUG
if (in && fin) error ("Cannot feed data to a finished stream.\n");
#endif
fin = 1;
if (in) end += in;
tail->data = scan (end, 1);
tail = tail->next = Link();
}
}
void reset()
{
head = Link();
tail = head;
next_token = 0;
end = "";
fin = 0;
}
mixed read()
{
while (head->next)
if (next_token >= sizeof (head->data)) {
next_token = 0;
head = head->next;
}
else return head->data[next_token++];
return Void;
}
void unread (mixed... put_back)
{
int i = sizeof (put_back);
while (i) head->data[--next_token] = put_back[--i];
if (i) {
Link l = Link();
l->next = head, head = l;
l->data = allocate (next_token = [int] max (i - 32, 0)) + put_back[..--i];
}
}
array read_all()
{
array data;
if (next_token) {
data = head->data[next_token..];
head = head->next;
next_token = 0;
}
else data = ({});
while (head->next) {
data += head->data;
head = head->next;
}
return data;
}
int finished()
{
return fin;
}
}
static program PHtml;
static program PExpr;
void _fix_module_ref (string name, mixed val)
{
mixed err = catch {
switch (name) {
case "PHtml": PHtml = [program] val; break;
case "PExpr": PExpr = [program] val; break;
case "empty_tag_set": empty_tag_set = [object(TagSet)] val; break;
default: error ("Herk\n");
}
};
if (err) werror (describe_backtrace (err));
}
|