2000-01-14
2000-01-14 05:14:48 by Martin Stjernholm <mast@lysator.liu.se>
-
01e43be1fbfc62af1e24a821b0eeb1192093be6c
(284 lines)
(+181/-103)
[
Show
| Annotate
]
Branch: 5.2
Better error handling. Some fixes to the object-as-scope stuff. Frames
now always get all the passed arguments, not only those mentioned in
req_arg_types and opt_arg_types. Fixes to variable evaluation. New
type t_html.
Rev: server/etc/modules/RXML.pmod/module.pmod:1.16
2:
//!
//! Created 1999-07-30 by Martin Stjernholm.
//!
- //! $Id: module.pmod,v 1.15 2000/01/12 20:14:52 mast Exp $
+ //! $Id: module.pmod,v 1.16 2000/01/14 05:14:48 mast Exp $
//! Kludge: Must use "RXML.refs" somewhere for the whole module to be
//! loaded correctly.
82:
// Internals.
- mixed _handle_tag (TagSetParser parser, mapping(string:string) args,
+ array _handle_tag (TagSetParser parser, mapping(string:string) args,
void|string content)
// Callback for tag set parsers. Returns a sequence of result values
// to be added to the result queue. Note that this function handles
106: Inside #if defined(DEBUG)
if (!ustate) ustate = ctx->unwind_state = ([]);
#ifdef DEBUG
if (err != frame)
- parse_error ("Internal error: Unexpected unwind object catched.\n");
+ error ("Internal error: Unexpected unwind object catched.\n");
if (ustate[parser])
- parse_error ("Internal error: Clobbering unwind state for parser.\n");
+ error ("Internal error: Clobbering unwind state for parser.\n");
#endif
ustate[parser] = ({err});
err = parser;
392:
class Scope
//! Interface for objects that emulates a scope mapping.
{
- mixed `[] (string var);
- array(string) _indices();
- void m_delete (string var);
+ mixed `[] (string var, void|Context ctx, void|string scope_name)
+ {rxml_error ("Cannot query variable" + _in_the_scope (scope_name) + ".\n");}
+
+ mixed `[]= (string var, mixed val, void|Context ctx, void|string scope_name)
+ {rxml_error ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");}
+
+ array(string) _indices (void|Context ctx, void|string scope_name)
+ {rxml_error ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");}
+
+ void m_delete (string var, void|Context ctx, void|string scope_name)
+ {rxml_error ("Cannot delete variable" + _in_the_scope (scope_name) + ".\n");}
+
+ 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 "";
}
-
+ }
-
+ #define SCOPE_TYPE mapping(string:mixed)|object(Scope)
-
+
class Context
//! A parser context. This contains the current variable bindings and
//! so on. The current context can always be retrieved with
416:
int type_check;
//! Whether to do type checking.
+ int got_error;
+ //! Nonzero if an RXML error is encountered.
+
TagSet tag_set;
//! The current tag set that will be inherited by subparsers.
423:
//! Nonzero if tag_set is a copy local to this context. A local tag
//! set that imports the old tag set is created whenever need be.
- #define SCOPE_TYPE mapping(string:mixed)|object(Scope)
-
+
mixed get_var (string var, void|string scope_name)
//! Returns the value a variable in the specified scope, or the
//! current scope if none is given. Returns zero with zero_type 1 if
432:
{
if (SCOPE_TYPE vars = scopes[scope_name || ""]) {
mixed val;
- if (zero_type (val = vars[var])) return ([])[0];
+ if (mappingp (vars)) {
+ val = ([object(Scope)] vars)->`[] (var, this_object(), scope_name || "");
+ if (val == Void) return ([])[0];
+ }
+ else if (zero_type (val = vars[var])) return ([])[0];
else if (objectp (val) && ([object] val)->rxml_var_eval)
- return ([object(Value)] val)->rxml_var_eval (this_object(), var, scope_name);
+ return ([object(Value)] val)->
+ rxml_var_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");
+ else if (scope_name) rxml_error ("Unknown scope %O.\n", scope_name);
+ else rxml_error ("No current scope.\n");
}
mixed set_var (string var, mixed val, void|string scope_name)
446:
//! current scope if none is given. Returns val.
{
if (SCOPE_TYPE vars = scopes[scope_name || ""])
+ if (objectp (vars))
+ return ([object(Scope)] vars)->`[]= (var, val, this_object(), scope_name || "");
+ else
return vars[var] = val;
- else if (scope_name) error ("Unknown scope %O.\n", scope_name);
- else error ("No current scope.\n");
+ else if (scope_name) rxml_error ("Unknown scope %O.\n", scope_name);
+ else rxml_error ("No current scope.\n");
}
void delete_var (string var, void|string scope_name)
456:
//! if none is given.
{
if (SCOPE_TYPE vars = scopes[scope_name || ""])
- if (objectp (vars)) ([object(Scope)] vars)->m_delete (var);
- else m_delete ([mapping(string:mixed)] vars, var);
- else if (scope_name) error ("Unknown scope %O.\n", scope_name);
- else error ("No current scope.\n");
+ if (objectp (vars))
+ ([object(Scope)] vars)->m_delete (var, this_object(), scope_name || "");
+ else
+ m_delete ([mapping(string:mixed)] vars, var);
+ else if (scope_name) rxml_error ("Unknown scope %O.\n", scope_name);
+ else rxml_error ("No current scope.\n");
}
array(string) list_var (void|string scope_name)
467:
//! the current scope if none is given.
{
if (SCOPE_TYPE vars = scopes[scope_name || ""])
- return indices (vars);
- else if (scope_name) error ("Unknown scope %O.\n", scope_name);
- else error ("No current scope.\n");
+ if (objectp (vars))
+ return ([object(Scope)] vars)->_indices (this_object(), scope_name || "");
+ else
+ return indices ([mapping(string:mixed)] vars);
+ else if (scope_name) rxml_error ("Unknown scope %O.\n", scope_name);
+ else rxml_error ("No current scope.\n");
}
array(string) list_scopes()
550:
new_runtime_tags->remove_tags[tag] = 1;
}
- static constant rxml_errmsg_prefix = "RXML parser error: ";
-
- void error (string msg, mixed... args)
- //! Throws an error with a dump of the parser stack.
+ void rxml_error (string msg, mixed... args)
+ //! Throws an RXML error with a dump of the parser stack.
{
if (sizeof (args)) msg = sprintf (msg, @args);
- msg = rxml_errmsg_prefix + msg;
+ msg = rxml_errmsg_prefix + ": " + msg;
for (Frame f = frame; f; f = f->up) {
if (f->tag) msg += "<" + f->tag->name;
else if (!f->up) break;
575:
throw (({msg, b[..sizeof (b) - 2]}));
}
- string handle_exception (mixed err)
- //! This function gets any exception that is catched at the top
- //! level of the parser. The returned value is added to the result,
- //! if possible.
+ void handle_exception (mixed err, PCode|Parser evaluator)
+ //! This function gets any exception that is catched during
+ //! evaluation. evaluator is the object that catched the error.
{
- if (arrayp (err) && sizeof ([array] err) == 2) {
- array err = [array] err;
- if (stringp (err[0]) &&
- ([string] err[0])[..sizeof (rxml_errmsg_prefix) - 1] == rxml_errmsg_prefix)
+ got_error = 1;
+ string msg = describe_error (err);
+ if (msg[..sizeof (rxml_errmsg_prefix) - 1] == rxml_errmsg_prefix) {
// An RXML error.
-
+ while (evaluator->_parent) evaluator = evaluator->_parent;
if (id && id->conf)
- return ([function(mixed:string)]
- ([object] id->conf)->report_rxml_error) (err);
+ msg = ([function(mixed,Type:string)]
+ ([object] id->conf)->report_rxml_error) (err, evaluator->type);
else {
#ifdef MODULE_DEBUG
report_notice (describe_backtrace (err));
#else
- report_notice (err[0]);
+ report_notice (msg);
#endif
- return [string] err[0];
+
}
-
+ if (evaluator->type->free_text && evaluator->report_error)
+ evaluator->report_error (msg);
}
- throw (err);
+ else throw (err);
}
// Internals.
-
+ constant rxml_errmsg_prefix = "RXML parser error";
+
private string error_print_val (mixed val)
{
if (arrayp (val)) return "array";
733:
set_context (ctx); \
if (ctx) { \
if (ctx->in_use && __old_ctx != ctx) \
- parse_error ("Attempt to use context asynchronously.\n"); \
+ error ("Attempt to use context asynchronously.\n"); \
ctx->in_use = 1; \
}
753:
#endif
- void parse_error (string msg, mixed... args)
- //! Tries to throw an error with error() in the current context to
- //! include the frame stack.
+ void rxml_error (string msg, mixed... args)
+ //! Tries to throw an error with rxml_error() in the current context.
{
Context ctx = get_context();
- if (ctx && ctx->error)
- ctx->error (msg, @args);
+ if (ctx && ctx->rxml_error)
+ ctx->rxml_error (msg, @args);
else {
if (sizeof (args)) msg = sprintf (msg, @args);
- msg = "RXML parser error (no context): " + msg;
+ msg = Context.rxml_errmsg_prefix + " (no context): " + msg;
array b = backtrace();
throw (({msg, b[..sizeof (b) - 2]}));
}
975:
//! Services.
- void error (string msg, mixed... args)
- //! Throws an error with a backtrace from the current context.
+ void rxml_error (string msg, mixed... args)
+ //! Throws an RXML error from the current context.
{
- parse_error (msg, @args);
+ get_context()/*HMM*/->rxml_error (msg, @args);
}
void terminate()
1166:
ctx->frame = this;
if (raw_args) {
- args = ([]);
+ args = raw_args;
mapping(string:Type) atypes;
if (tag->req_arg_types) {
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 " +
+ rxml_error ("Required " +
(sizeof (missing) > 1 ?
"arguments " + String.implode_nicely (missing) + " are" :
"argument " + missing[0] + " is") + " missing.\n");
1185:
if (mixed err = catch {
foreach (indices (atypes), string arg)
args[arg] = atypes[arg]->eval (
- raw_args[arg], ctx, 0, 1); // Should currently NOT unwind.
+ raw_args[arg], ctx, 0, parser, 1); // Should currently NOT unwind.
}) {
if (objectp (err) && ([object] err)->thrown_at_unwind)
error ("Can't save parser state when evaluating arguments.\n");
1207:
if (!result_type) {
Type ptype = parser->type;
foreach (tag->result_types, Type rtype)
- if (rtype->subtype_of (ptype)) {result_type = rtype; break;}
+ if (ptype->subtype_of (rtype)) {result_type = rtype; break;}
if (!result_type) // Sigh..
- error ("Tag returns " +
+ rxml_error (
+ "Tag returns " +
String.implode_nicely ([array(string)] tag->result_types->name, "or") +
" but " + [string] parser->type->name + " is expected.\n");
}
1382:
ustate[this] = ({err, fn, iter, raw_content, subparser, piece, exec, tags_added,
ctx->new_runtime_tags});
}
- else action = "throw";
+ else {
+ ctx->handle_exception (err, parser); // May throw.
+ action = "return";
+ }
switch (action) {
case "break": // Throw and handle in parent frame.
1394:
case "continue": // Continue in this frame through tail recursion.
_eval (parser);
return;
- case "throw": // Any old exception.
- throw (err);
+ case "return": // A normal return.
+ break;
default:
error ("Internal error: Don't you come here and %O on me!\n", action);
}
}
- else {
+
if (tags_added)
ctx->tag_set->imported -= ({/*[object(TagSet)]HMM*/ this->additional_tags});
ctx->frame = up;
}
- }
+
string _sprintf()
{
1458: Inside #if defined(DEBUG)
if (objectp (err) && ([object] err)->thrown_at_unwind) {
#ifdef DEBUG
if (err != this_object())
- parse_error ("Internal error: Unexpected unwind object catched.\n");
+ error ("Internal error: Unexpected unwind object catched.\n");
#endif
if (!context->unwind_state) context->unwind_state = ([]);
context->unwind_state->top = err;
}
- else
- if (context) {
- if (string msg = context->handle_exception (err))
- if (type->free_text && this_object()->write_out)
- ([function(string:void)] this_object()->write_out) (msg);
- }
+ else if (context)
+ context->handle_exception (err, this_object()); // May throw.
else throw (err);
return res;
}
1496: Inside #if defined(DEBUG)
if (objectp (err) && ([object] err)->thrown_at_unwind) {
#ifdef DEBUG
if (err != this_object())
- parse_error ("Internal error: Unexpected unwind object catched.\n");
+ error ("Internal error: Unexpected unwind object catched.\n");
#endif
if (!context->unwind_state) context->unwind_state = ([]);
context->unwind_state->top = err;
}
- else
- if (context) {
- if (string msg = context->handle_exception (err))
- if (type->free_text && this_object()->write_out)
- ([function(string:void)] this_object()->write_out) (msg);
- }
+ else if (context)
+ context->handle_exception (err, this_object()); // May throw.
else throw (err);
}
-
+ array handle_var (string varref)
+ // Parses and evaluates a possible variable reference, with the
+ // appropriate error handling.
+ {
+ // We're always evaluating here, so context is always set.
+ array(string) split = varref / ".";
+ if (sizeof (split) == 2)
+ if (mixed err = catch {
+ mixed val;
+ if (zero_type (val = context->get_var (split[1], split[0]))) // May throw.
+ return ({});
+ if (type->free_text) val = (string) val;
+ else return val == Void ? ({}) : ({val});
+ }) {
+ context->handle_exception (err, this_object()); // May throw.
+ return ({});
+ }
+ return type->free_text ? 0 : ({});
+ }
+
//! Interface.
Context context;
1542:
//! data may be given. It should work to call this on an already
//! finished stream if no argument is given to it.
+ optional void report_error (string msg);
+ //! Used to report errors to the end user through the output. This
+ //! is only called when the type allows free text. msg should be
+ //! stored in the output queue to be returned by eval().
+
optional mixed read();
//! Define to allow streaming operation. Returns the evaluated
//! result so far, but does not do any evaluation. Returns Void if
1585:
Parser _parent;
// The parent parser if this one is nested.
+ Stdio.File _source_file;
+ mapping _defines;
+ // These two are compatibility kludges for use with parse_rxml().
+
string _sprintf() {return "RXML.Parser";}
}
1593:
//! Interface class for parsers that evaluates using the tag set. It
//! provides the evaluation and compilation functionality. The parser
//! should call Tag._handle_tag() from feed() and finish() for every
- //! encountered tag, and Context.get_var() for encountered variable
+ //! encountered tag, and Parser.handle_var() for encountered variable
//! references. It must be able to continue cleanly after throw() from
//! Tag._handle_tag().
{
1729:
//! sequential and use strings.
void type_check (mixed val);
- //! Checks whether the given value is a valid one of this type.
- //! Errors are thrown with parse_error().
+ //! Checks whether the given value is a valid one of this type. Type
+ //! errors are thrown with RXML.rxml_error().
Type clone()
//! Returns a copy of the type.
1836:
return p;
}
- mixed eval (string in, void|Context ctx, void|TagSet tag_set, void|int dont_switch_ctx)
+ mixed eval (string in, void|Context ctx, void|TagSet tag_set,
+ void|Parser|PCode parent, void|int dont_switch_ctx)
//! Convenience function to parse and evaluate the value in the
//! given string. If a context isn't given, the current one is used.
//! The current context and ctx are assumed to be the same if
1847:
if (_parser_prog == PNone) res = in;
else {
Parser p = get_parser (ctx, tag_set);
+ p->_parent = parent;
if (dont_switch_ctx) p->finish (in); // Optimize the job in p->write_end().
else p->write_end (in);
res = p->eval();
1894:
}
- Type t_text = class
+ static class TypeAny
+ //! A completely unspecified nonsequential type.
+ {
+ inherit Type;
+ constant name = "*";
+ string _sprintf() {return "RXML.t_any";}
+ }
+ TypeAny t_any = TypeAny();
+
+ static class TypeText
//! The standard type for generic document text.
{
inherit Type;
1903:
constant empty_value = "";
constant free_text = 1;
string _sprintf() {return "RXML.t_text";}
- }();
+ }
+ TypeText t_text = TypeText();
-
- Type t_any = class
- //! A completely unspecified nonsequential type.
+ static class TypeHtml
+ //! The standard type for generic document text.
{
- inherit Type;
- constant name = "*";
- string _sprintf() {return "RXML.t_any";}
- }();
+ inherit TypeText;
+ constant name = "text/html";
+ string _sprintf() {return "RXML.t_html";}
+ }
+ TypeHtml t_html = TypeHtml();
// P-code compilation and evaluation.
1951:
//! function will receive a context to do the evaluation in.
string _sprintf() {return "RXML.PCode";}
+
+
+ // Internals.
+
+ void report_error (string msg)
+ {
+ // FIXME
}
-
+ PCode|Parser _parent;
+ // The parent evaluator if this one is nested.
+ }
-
+
//! Some parser tools.
static class VoidType