(*
* <COPYRIGHT>
* Copyright 2002 Joachim Schrod Network and Publication Consultance GmbH, Gerd Stolpmann
*
* <GPL>
* This file is part of WDialog.
*
* WDialog is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* WDialog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WDialog; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* </>
*)
(* $Id: wd_extension.ml,v 3.4 2002-09-25 00:19:20 stolpmann Exp $
* ----------------------------------------------------------------------
*
*)
open Pcaml;;
(* Syntax extension:
*
* You can now write
*
* class name ... = interactive object ... end
*
* The new keyword "interactive" enables the following special features:
*
* - Methods with names "prepare_*" are collected, and a dispatcher method
* "prepare_page" is generated and added
* - Methods with names "handle_*" are collected, and a dispatcher method
* "handle" is generated and added
* Special notations:
* - _[variable]
* - _[variable:string] or _[variable:s]
* - _[variable:enum] or _[variable:e]
* - _[variable:dynenum] or _[variable:y]
* - _[variable:alist] or _[variable:a]
* - _[variable:dialog] or _[variable:d]
* - _[variable.( expr ):string] (etc.)
*
* - _[variable] <- value
* - _[variable:string] <- value (etc.)
*)
(* Note:
* MLast.CrMth has four arguments until O'Caml 3.04. Later versions take
* five arguments, as in the following code.
*)
let find_prepare_page_methods nodes =
List.map
(fun node ->
match node with
MLast.CrMth(_,name,_,_,_) ->
name
| _ ->
assert false)
(List.filter
(fun node ->
match node with
MLast.CrMth(_,name,_,_,_) ->
String.length name >= 9 & String.sub name 0 8 = "prepare_"
| _ ->
false
)
nodes
)
;;
let find_handle_methods nodes =
List.map
(fun node ->
match node with
MLast.CrMth(_,name,_,_,_) ->
name
| _ ->
assert false)
(List.filter
(fun node ->
match node with
MLast.CrMth(_,name,_,_,_) ->
(String.length name >= 8 & String.sub name 0 7 = "handle_")
or (name = "handle")
| _ ->
false
)
nodes
)
;;
let make_prepare_page_dispatcher loc names =
(* Creates a method as follows:
* method prepare_page =
* match self # page_name with
* name1 -> self # prepare_<name1>
* | name2 -> self # prepare_<name2>
* ...
* | _ -> ()
*)
let cases =
List.map
(fun name ->
let pagename = String.sub name 8 (String.length name - 8) in
(* <:match_case< $lid:name$ -> self # $lid:methodname$>> *)
( <:patt< $str:pagename$>>, (* pattern *)
None, (* "when" clause *)
<:expr< self # $name$ ()>> (* -> expression *)
)
)
names
@ [ <:patt< _ >>, None, <:expr< () >> ]
in
let names' = List.map (fun s ->
let s' =
String.sub s 8 (String.length s - 8) in
<:expr< $str:s'$ >>
) names in
let namelist = Pa_o.mklistexp loc None names' in
let body = <:expr<
let pages = self # page_names in
let _ = List.iter (fun n ->
if not (List.mem n pages)
then
raise
(Wd_types.Runtime_error
("No page defined for method prepare_" ^ n))
else
()
)
$namelist$ in
match self # page_name with [ $list:cases$ ]>> in
<:class_str_item< method prepare_page() = $body$>>
;;
let make_handle_dispatcher loc names =
(* Creates a method as follows:
* method handle =
* match self # page_name with
* name1 -> self # handle_<name1>
* | name2 -> self # handle_<name2>
* ...
* | _ -> ()
*)
let cases =
List.map
(fun name ->
let pagename = String.sub name 7 (String.length name - 7) in
(* <:match_case< $lid:name$ -> self # $lid:methodname$>> *)
( <:patt< $str:pagename$>>, (* pattern *)
None, (* "when" clause *)
<:expr< self # $name$ ()>> (* -> expression *)
)
)
names
@ [ <:patt< _ >>, None, <:expr< () >> ]
in
let names' = List.map (fun s ->
let s' =
String.sub s 7 (String.length s - 7) in
<:expr< $str:s'$ >>
) names in
let namelist = Pa_o.mklistexp loc None names' in
let body = <:expr<
let pages = self # page_names in
let _ = List.iter (fun n ->
if not (List.mem n pages)
then
raise
(Wd_types.Runtime_error
("No page defined for method handle_" ^ n))
else
()
)
$namelist$ in
match self # page_name with [ $list:cases$ ]>> in
<:class_str_item< method handle () = $body$>>
;;
let modify_class_structure loc cf =
let names = find_prepare_page_methods cf in
(* If there is already a method "prepare_page", output a warning and
* leave the method as it is.
*)
let cf' =
if List.mem "prepare_page" names then begin
(* Output warning *)
!warning loc "This object already defines the method 'prepare_page'; it is not generated";
cf
end
else begin
(* Add the generated method: *)
!warning loc ("FYI: Generating method 'prepare_page' that dispatches among: " ^ (String.concat ", " names));
let m = make_prepare_page_dispatcher loc names in
m :: cf
end
in
(* Same for "handle" method: *)
let names = find_handle_methods cf' in
if List.mem "handle" names then begin
(* Output warning *)
!warning loc "This object already defines the method 'handle'; it is not generated";
cf'
end
else begin
(* Add the generated method: *)
!warning loc ("FYI: Generating method 'handle' that dispatches among: " ^
(String.concat ", " names));
let m = make_handle_dispatcher loc names in
m :: cf'
end
;;
EXTEND
GLOBAL: class_expr expr;
(* The new "interactive object": *)
class_expr: AFTER "simple"
[ [ "interactive";
"object";
cspo = OPT class_self_patt; cf = class_structure; "end" ->
let cf' = modify_class_structure loc cf in
<:class_expr< object $cspo$ $list:cf'$ end >>
]
];
(* class_self_patt and class_structure are simply copied from the
* distributed pa_a.ml file of camlp4. We must copy these rules because
* they are only locally defined.
*)
class_self_patt:
[ [ "("; p = patt; ")" -> p
| "("; p = patt; ":"; t = ctyp; ")" -> <:patt< ($p$ : $t$) >> ]
];
class_structure:
[ [ cf = LIST0 class_str_item -> cf ] ];
(* The :"var"...: expressions: *)
expr: BEFORE "simple"
[ [
"_";"["; name = LIDENT; "]"; "<-" ->
<:expr< self # set_variable $str:name$ >>
| "_";"["; name = LIDENT; ":"; selector = LIDENT; "]"; "<-" ->
begin match selector with
("s"|"string") ->
<:expr<
(fun x ->
self # set_variable
$str:name$
(Wd_types.String_value x))
>>
| ("e"|"enum") ->
<:expr<
(fun x ->
self # set_variable
$str:name$
(Wd_types.Enum_value x))
>>
| ("y"|"dynenum"|"dyn_enum") ->
<:expr<
(fun x ->
self # set_variable
$str:name$
(Wd_types.Dyn_enum_value x))
>>
| ("d"|"dialog") ->
<:expr<
(fun x ->
self # set_variable
$str:name$
(Wd_types.Dialog_value x))
>>
| ("a"|"alist") ->
<:expr<
(fun x ->
self # set_variable
$str:name$
(Wd_types.Alist_value x))
>>
| _ ->
Stdpp.raise_with_loc loc (Failure ("Unknown selector " ^ selector))
end
| "_";"["; name = LIDENT; "]" ->
<:expr< self # variable $str:name$ >>
| "_";"["; name = LIDENT; ":"; selector = LIDENT; "]" ->
begin match selector with
("s"|"string") ->
<:expr< self # string_variable $str:name$ >>
| ("e"|"enum") ->
<:expr< self # enum_variable $str:name$ >>
| ("y"|"dynenum"|"dyn_enum") ->
<:expr< self # dyn_enum_variable $str:name$ >>
| ("d"|"dialog") ->
<:expr< self # dialog_variable $str:name$ >>
| ("a"|"alist") ->
<:expr< self # alist_variable $str:name$ >>
| _ ->
Stdpp.raise_with_loc loc (Failure ("Unknown selector " ^ selector))
end
| "_";"["; name = LIDENT; "."; "("; e = expr; ")";
":"; selector = LIDENT; "]" ->
begin match selector with
("s"|"string") ->
<:expr< self # lookup_string_variable $str:name$ $e$ >>
| ("e"|"enum") ->
<:expr< self # lookup_enum_variable $str:name$ $e$ >>
| ("y"|"dynenum"|"dyn_enum") ->
<:expr< self # lookup_dyn_enum_variable $str:name$ $e$ >>
| ("d"|"dialog") ->
<:expr< self # lookup_dialog_variable $str:name$ $e$ >>
| ("a"|"alist") ->
Stdpp.raise_with_loc loc (Failure "Selector 'alist' not possible after lookup")
| _ ->
Stdpp.raise_with_loc loc (Failure ("Unknown selector " ^ selector))
end
]
];
END;;
(* ======================================================================
* History:
*
* $Log: wd_extension.ml,v $
* Revision 3.4 2002-09-25 00:19:20 stolpmann
* Works now (ONLY) for O'Caml 3.06
*
* Revision 3.3 2002/02/14 16:16:01 stolpmann
* Added copyright notice.
*
* Revision 3.2 2002/02/13 00:17:49 stolpmann
* Updated
*
* Revision 3.1 2002/02/12 20:35:03 stolpmann
* Initial revision.
*
* Revision 1.1 2002/01/14 15:04:00 gerd
* Syntax change: using _(var) instead of `var`
*
* Revision 1.1 2000/04/13 17:43:08 gerd
* Initial revision.
*
*
*)