(* $Id: pxp_entity_manager.ml 673 2004-06-02 22:07:53Z gerd $
* ----------------------------------------------------------------------
*
*)
(* An 'entity_manager' is a stack of entities, where the topmost entity
* is the currently active entity, the second entity is the entity that
* referred to the active entity, and so on.
*
* The entity_manager can communicate with the currently active entity.
*
* The entity_manager provides an interface for the parser; the functions
* returning the current token and the next token are exported.
*)
open Pxp_entity
open Pxp_dtd
open Pxp_reader
class entity_manager
(init_entity : entity) (init_dtd : dtd) =
let init_r =
match init_entity # resolver with
Some r -> r
| None -> failwith "Pxp_entity_manager.entity_manager: initial entity does not have a resolver"
in
object (self)
val dtd = init_dtd
val top_resolver = init_r
val top_entity = init_entity
val mutable entity_stack = Stack.create()
val mutable current_entity = init_entity
val mutable current_entity's_full_name = lazy (init_entity # full_name)
val mutable current_resolver = init_r
val mutable yy_get_next_ref = ref (fun () -> assert false)
initializer
init_entity # set_manager (self :>
< current_entity : entity;
pop_entity : unit -> unit;
push_entity : entity -> unit >
);
yy_get_next_ref := (fun () -> init_entity # next_token)
method dtd = dtd
method push_entity e =
e # set_manager (self :>
< current_entity : entity;
pop_entity : unit -> unit;
push_entity : entity -> unit >
);
Stack.push (current_entity, current_entity's_full_name, current_resolver) entity_stack;
current_entity <- e;
current_entity's_full_name <- lazy (e # full_name);
( match e # resolver with
None -> () (* e is an internal entity *)
| Some r -> current_resolver <- r;
);
yy_get_next_ref := (fun () -> e # next_token);
method pop_entity() =
(* May raise Stack.Empty *)
let e, e_name, e_res = Stack.pop entity_stack in
current_entity <- e;
current_entity's_full_name <- e_name;
current_resolver <- e_res;
yy_get_next_ref := (fun () -> e # next_token);
method position_string =
(* Gets a string describing the position of the last token;
* includes an entity backtrace
*)
let b = Buffer.create 200 in
Buffer.add_string b
("In entity " ^ current_entity # full_name
^ ", at line " ^ string_of_int (current_entity # line)
^ ", position " ^ string_of_int (current_entity # column)
^ ":\n");
Stack.iter
(fun (e, e_name, e_res) ->
Buffer.add_string b
("Called from entity " ^ Lazy.force e_name
^ ", line " ^ string_of_int (e # line)
^ ", position " ^ string_of_int (e # column)
^ ":\n");
)
entity_stack;
Buffer.contents b
method position =
(* Returns the triple (full_name, line, column) of the last token *)
Lazy.force current_entity's_full_name,
current_entity # line,
current_entity # column
method current_entity_counts_as_external =
(* Whether the current entity counts as external to the main
* document for the purpose of stand-alone checks.
*)
(* TODO: improve performance *)
let is_external = ref false in
let check (e, _, _) =
if e # counts_as_external then begin
is_external := true;
end;
in
check (current_entity,(),());
Stack.iter check entity_stack;
!is_external
method current_entity = current_entity
method current_resolver = current_resolver
(* The resolver of the most recent external entity *)
method top_entity = top_entity
method top_resolver = top_resolver
method yy_get_next_ref = yy_get_next_ref
(* Methods for out-of-order lexing: *)
method current_lexer_obj = current_entity # lexer_obj
method current_line_column = (current_entity # line,
current_entity # column)
method update_line_column (l,c) =
current_entity # set_line_column l c
method pop_entity_until until_ent =
(* pops entities from the stack and ensures that they are closed until
* [until_ent] is the top element. All entities that are taken from
* the stack are closed. [until_ent] is not closed.
*)
try
while current_entity <> until_ent do
if current_entity # is_open then
ignore(current_entity # close_entity);
self # pop_entity();
done
with
Stack.Empty -> ()
end
;;