~ubuntu-branches/ubuntu/hardy/pxp/hardy

« back to all changes in this revision

Viewing changes to examples/eventparser/sample.ml

  • Committer: Bazaar Package Importer
  • Author(s): Stefano Zacchiroli
  • Date: 2005-03-29 11:06:39 UTC
  • mfrom: (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050329110639-5p39hz1d4aq3r2ec
Tags: 1.1.95-6
* Rebuilt against ocaml 3.08.3
* No longer built with wlex support (since wlex is no longer supported
  upstream and corresponding package has been removed from the debian
  archive)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* $Id: sample.ml 662 2004-05-25 20:57:28Z gerd $
 
2
 * ----------------------------------------------------------------------
 
3
 *
 
4
 *)
 
5
 
 
6
 
 
7
(**********************************************************************)
 
8
(* Examples for event-based parsing ("SAX-like parsing")              *)
 
9
(**********************************************************************)
 
10
 
 
11
 
 
12
open Pxp_yacc
 
13
open Pxp_lexer_types
 
14
open Pxp_types
 
15
open Expr
 
16
open Exprlex
 
17
open Printf
 
18
 
 
19
 
 
20
(* dump_event: dumps a single parsing event *)
 
21
 
 
22
let dump_event =
 
23
  function
 
24
      E_start_doc(v,sa,dtd) ->
 
25
        printf "E_start_doc version=%s standalone=%b\n" v sa
 
26
    | E_end_doc ->
 
27
        printf "E_end_doc\n"
 
28
    | E_start_tag(name,attlist,_) ->
 
29
        printf "E_start_tag %s %s\n" name 
 
30
          (String.concat " " (List.map (fun (n,v) -> n ^ "=" ^ v) attlist))
 
31
    | E_end_tag(name,_) ->
 
32
        printf "E_end_tag %s\n" name
 
33
    | E_char_data data ->
 
34
        printf "E_char_data %s\n" data
 
35
    | E_pinstr(target,data) ->
 
36
        printf "E_pinstr %s %s\n" target data
 
37
    | E_comment data ->
 
38
        printf "E_comment %s\n" data
 
39
    | E_position(ent,line,col) ->
 
40
        printf "E_position %s line=%d col=%d\n" ent line col
 
41
    | E_error e ->
 
42
        printf "E_error %s\n" (Printexc.to_string e)
 
43
    | E_end_of_stream ->
 
44
        printf "E_end_of_stream\n"
 
45
;;
 
46
 
 
47
 
 
48
(* parse: prints the events while parsing the passed string *)
 
49
 
 
50
let parse s =
 
51
  process_entity
 
52
    default_config
 
53
    (`Entry_document[])
 
54
    (create_entity_manager default_config (from_string s))
 
55
    dump_event;
 
56
  flush stdout
 
57
;;
 
58
 
 
59
 
 
60
(* curly_parse: demonstrates how to use escape_contents. The character
 
61
 * { escapes from normal parsing and calls [escape]. The arithmetic
 
62
 * expression inside { ... } is evaluated, and the result is taken 
 
63
 * as the text content.
 
64
 * Try: curly_parse "<a>{123 + 5}</a>"
 
65
 *      curly_parse "<a>{{123 + 5}}</a>"
 
66
 *      curly_parse "<a>{123 + 5</a>}</a>"
 
67
 *)
 
68
 
 
69
let inc_col (l,c) = (l,c+1);;
 
70
let inc_line (l,c) = (l+1,0);;
 
71
let add_col n (l,c) = (l,c+n);;
 
72
 
 
73
 
 
74
let curly_parse s =
 
75
  let parse_expr mng =
 
76
    (* We get now the current lexical buffer of PXP, and use it for
 
77
     * our own parsing. In particular, we call Expr.topexpr
 
78
     * to parse the arithmetic expression. While parsing,
 
79
     * we track the current line and column (function [scan]).
 
80
     *)
 
81
    let line_col = ref (mng # current_line_column) in
 
82
    (* Note: current_line_column returns the position of the beginning of
 
83
     * the token that has been parsed last. The last token was "{" (Lcurly).
 
84
     * So we must add 1 to this position to get the position of the 
 
85
     * beginning of the next token.
 
86
     *)
 
87
    line_col := inc_col !line_col;
 
88
    let rec scan buf =
 
89
      match scan_expr buf with
 
90
          Newline -> 
 
91
            line_col := inc_line !line_col; 
 
92
            scan buf
 
93
        | Space ->
 
94
            let n = Lexing.lexeme_end buf - Lexing.lexeme_start buf in
 
95
            line_col := add_col n !line_col;
 
96
            scan buf
 
97
        | tok -> 
 
98
            let n = Lexing.lexeme_end buf - Lexing.lexeme_start buf in
 
99
            line_col := add_col n !line_col;
 
100
            tok
 
101
    in
 
102
    let lexbuf = mng # current_lexbuf in
 
103
    let value = topexpr scan lexbuf in
 
104
    printf "Result of expression: %d\n" value;
 
105
    mng # update_line_column !line_col;
 
106
    string_of_int value
 
107
  in
 
108
 
 
109
  let escape_contents tok mng =
 
110
    (* This function is called when "{", "{{", "}", or "}}" are found in
 
111
     * character node context.
 
112
     *)
 
113
    match tok with
 
114
        Lcurly ->
 
115
          (* "{" found: *)
 
116
          parse_expr mng
 
117
      | LLcurly -> 
 
118
          (* "{{" found: map to "{" *)
 
119
          "{"
 
120
      | Rcurly -> 
 
121
          (* "}" found *)
 
122
          failwith "Single brace } not allowed"
 
123
      | RRcurly -> 
 
124
          (* "}}" found: map to "}" *)
 
125
          "}"
 
126
      | _ -> assert false
 
127
  in
 
128
 
 
129
  let escape_attributes tok pos mng =
 
130
    (* This function is called when "{", "{{", "}", or "}}" are found in
 
131
     * attribute values.
 
132
     *)
 
133
    match tok with
 
134
        Lcurly ->
 
135
          (* "{" found: *)
 
136
          parse_expr mng
 
137
      | LLcurly ->
 
138
          (* "{{" found: *)
 
139
          "{"
 
140
      | Rcurly ->
 
141
          (* "}" found: *)
 
142
          failwith "Single brace } not allowed"
 
143
      | RRcurly ->
 
144
          (* "}}" found: *)
 
145
          "}"
 
146
      | _ ->
 
147
          assert false
 
148
  in
 
149
 
 
150
  let config = { default_config with 
 
151
                   escape_contents = Some escape_contents;
 
152
                   escape_attributes = Some escape_attributes;
 
153
               } in
 
154
  process_entity
 
155
    config
 
156
    (`Entry_document[])
 
157
    (create_entity_manager config (from_string s))
 
158
    dump_event;
 
159
  flush stdout
 
160
;;
 
161
 
 
162
 
 
163
(* rec_curly_parse: Here, escape_contents calls the XML parser recursively,
 
164
 * i.e. you can write XML documents inside curly braces, like in
 
165
 * rec_curly_parse "<A> { <B> x </B> } </A>" or
 
166
 * rec_curly_parse "<A att='{ <B> x </B> }'>y</A>"
 
167
 *
 
168
 * This is currently very experimental!
 
169
 *)
 
170
 
 
171
class any_entity_id = object end ;;
 
172
  (* An entity ID is an object without properties except identity *)
 
173
 
 
174
let rec_curly_parse s =
 
175
  let ent_id_guard = new any_entity_id in
 
176
  let base_config = default_config in
 
177
 
 
178
  let rec escape ent_id tok mng =
 
179
    (* ent_id: is the entity ID containing the last Lcurly, or ent_id_guard
 
180
     *  when there was none yet
 
181
     *)
 
182
    let current_ent = mng # current_entity in
 
183
    let current_ent_id = (current_ent :> entity_id) in
 
184
    match tok with
 
185
        Lcurly ->
 
186
          printf "Starting subparser...\n";
 
187
          (* Because [current_ent] is already open, we cannot use it as
 
188
           * top-level entity in [process_entity] (it is not possible to
 
189
           * open an entity several times). The solution is [sub_ent],
 
190
           * a so-called entity section that behaves much like [current_ent]
 
191
           * and shares most of the state with [current_ent], but pretends
 
192
           * it were an entity of its own.
 
193
           *)
 
194
          let sub_ent = new Pxp_entity.entity_section current_ent in
 
195
          let sub_ent_id = (sub_ent :> entity_id) in
 
196
          let sub_config =
 
197
            { base_config with
 
198
                escape_contents = Some (escape sub_ent_id) ;
 
199
                escape_attributes = Some (escape_att sub_ent_id) ;
 
200
            }
 
201
          in
 
202
          (* Pushing sub_ent makes it the top-level entity: *)
 
203
          mng # push_entity sub_ent;  
 
204
          process_entity
 
205
            sub_config
 
206
            (`Entry_document[])
 
207
            mng
 
208
            dump_event;
 
209
          assert(mng # current_entity = sub_ent);
 
210
          (* Pop sub_ent afterwards: *)
 
211
          mng # pop_entity ();
 
212
          ""
 
213
      | LLcurly -> "{"
 
214
      | Rcurly -> 
 
215
          if ent_id = ent_id_guard then
 
216
            (* A right curly brace without matching left curly brace *)
 
217
            failwith "Single brace } not allowed"
 
218
          else 
 
219
            if ent_id = current_ent_id then (
 
220
              (* The normal case: *)
 
221
              printf "Stopping subparser...\n";
 
222
              ignore(current_ent # close_entity);
 
223
              ""
 
224
                (* Causes that the current [process_entity] parser invocation
 
225
                 * terminates (if possible)
 
226
                 *)
 
227
            )
 
228
            else
 
229
              (* ent_id <> current_ent_id: This can happen if entities and
 
230
               * braces are used in strange ways:
 
231
               * <!DOCTYPE a [ <!ENTITY brace '}'> ]> 
 
232
               * <a> { <b>xxx</b> &brace; </a>
 
233
               *)
 
234
              failwith "Bad nesting of entities and braces {...}"
 
235
        
 
236
      | RRcurly -> "}"
 
237
      | _ -> assert false
 
238
  and escape_att ent_id tok pos mng = escape ent_id tok mng
 
239
  in
 
240
  let config = 
 
241
    { base_config with 
 
242
        escape_contents = Some (escape ent_id_guard);
 
243
        escape_attributes = Some (escape_att ent_id_guard);
 
244
    } in
 
245
 
 
246
  process_entity
 
247
    config
 
248
    (`Entry_document[])
 
249
    (create_entity_manager config (from_string s))
 
250
    dump_event;
 
251
  flush stdout
 
252
;;
 
253
 
 
254
 
 
255
(* parse_expr: An example for process_expr that parses the expressions
 
256
 * found in a string one after another.
 
257
 * Example:
 
258
 *      parse_expr "<?pi?><abc>def</abc> <qrt>def</qrt> "
 
259
 *
 
260
 * Unfortunately, we need the undocumented methods [open_entity] and
 
261
 * [close_entity] from [Pxp_entity], and we need the knowledge that
 
262
 * a [Begin_entity] token can be found at the beginning of the entity,
 
263
 * and that an [End_entity] token signals the end of the entity. Using
 
264
 * [process_expr] is quite low-level.
 
265
 *)
 
266
 
 
267
let parse_expr s =
 
268
  let config =
 
269
    { default_config with
 
270
        enable_pinstr_nodes = true;
 
271
        enable_comment_nodes = true
 
272
    } in
 
273
  let m = create_entity_manager config (from_string s) in
 
274
  m # current_entity # open_entity true Pxp_lexer_types.Content;
 
275
  let begin_entity_token = !(m # yy_get_next_ref)() in
 
276
  assert (begin_entity_token = Begin_entity);
 
277
  let tok = ref Ignore in       (* Ignore does not occur in the token stream *)
 
278
  while !tok <> End_entity do
 
279
    let first_token =
 
280
      if !tok <> Ignore then Some !tok else None in
 
281
    printf "*** Calling process_expr...\n";
 
282
    process_expr ?first_token ~following_token:tok config m dump_event;
 
283
  done;
 
284
  ignore(m # current_entity # close_entity);
 
285
;;
 
286