5 Jane Street Holding, LLC
7 email: mmottl\@janestcapital.com
8 WWW: http://www.janestcapital.com/ocaml
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 (* Path: Module for Substitutions within S-expressions *)
31 type el
= Pos
of int | Match
of string * int | Rec
of string
34 let illegal_atom loc sexp
=
35 failwith
(sprintf
"Path.%s: illegal atom: %s" loc
(Sexp.to_string sexp
))
37 let extract_pos_lst loc sexp ix lst
=
38 let rec loop acc n
= function
40 let sexp_str = Sexp.to_string sexp
in
42 sprintf
"Path.%s: illegal index %d in: %s" loc ix
sexp_str)
46 | None
-> List.rev_append acc t
47 | Some x
-> List.rev_append acc
(x
:: t
) in
49 else loop (h
:: acc
) (n
- 1) t
in
52 let extract_pos n
= function
54 let subst, el
= extract_pos_lst "extract_pos" sexp n lst
in
55 (fun x
-> List
(subst x
)), el
56 | Atom _
as sexp
-> illegal_atom "extract_pos" sexp
58 let extract_match tag arg_ix
= function
59 | List
(Atom str
as sexp
:: args
) when str
= tag
->
60 let subst, el
= extract_pos_lst "extract_match" (List args
) arg_ix args
in
61 (fun maybe_x
-> List
(sexp
:: subst maybe_x
)), el
63 let sexp_str = Sexp.to_string sexp
in
64 failwith
("Path.extract_match: unexpected nested list in: " ^
sexp_str)
65 | Atom _
as sexp
-> illegal_atom "extract_match" sexp
67 let extract_rec key
= function
69 let rec loop acc
= function
71 let sexp_str = Sexp.to_string sexp
in
73 sprintf
"Path.extract_rec: key \"%s\" not found in: %s"
75 | List
[Atom str
as sexp
; v
] :: rest
when str
= key
->
76 let subst x
= List
(List.rev_append acc
(List
[sexp
; x
] :: rest
)) in
78 | h
:: t
-> loop (h
:: acc
) t
in
80 | Atom _
as sexp
-> illegal_atom "extract_rec" sexp
84 let rec subst_option (sup_subst
, el
) rest
=
85 let sub_subst, sub_el
= subst_path el rest
in
86 let subst x
= sup_subst
(Some
(sub_subst x
)) in
89 and subst_path sexp
= function
90 | Pos n
:: t
-> subst_option (extract_pos n sexp
) t
91 | Match
(tag
, arg_ix
) :: t
-> subst_option (extract_match tag arg_ix sexp
) t
93 let rec_subst, el
= extract_rec key sexp
in
94 let sub_subst, sub_el
= subst_path el rest
in
95 let subst x
= rec_subst (sub_subst x
) in
100 let len = List.length lst
in
101 let str = String.create
len in
102 let rec loop ix
= function
103 | h
:: t
-> str.[ix
] <- h
; loop (ix
+ 1) t
107 let fail_parse msg
= failwith
("Path.parse: " ^ msg
)
110 let len = String.length
str in
111 if len = 0 then fail_parse "path empty"
113 let rec loop acc dot_ix
=
114 match str.[dot_ix
] with
116 let dot_ix1 = dot_ix
+ 1 in
117 if dot_ix1 = len then List.rev acc
119 let rec parse_dot acc str_acc ix
=
121 List.rev_append acc
[Rec
(implode (List.rev str_acc
))]
125 let rec parse_index index_acc ix
=
126 if ix
= len then fail_parse "EOF reading index"
128 match str.[ix
], index_acc
with
129 | '
0'
..'
9'
as c
, None
->
130 parse_index (Some
(int_of_char c
- 48)) (ix
+ 1)
131 | '
0'
..'
9'
as c
, Some index_acc
->
133 Some
(10 * index_acc
+ int_of_char c
- 48) in
134 parse_index new_index_acc (ix
+ 1)
135 | '
]'
, None
-> fail_parse "empty index"
136 | '
]'
, Some index_acc
->
138 if str_acc
= [] then Pos index_acc
140 Match
(implode (List.rev str_acc
), index_acc
) in
142 if ix1 = len then List.rev_append acc
[path_el]
143 else loop (path_el :: acc
) ix1
146 sprintf
"illegal character in index: %c" c
) in
147 parse_index None
(ix
+ 1)
150 if ix1 = len then fail_parse "EOF after escape"
151 else parse_dot acc
(str.[ix1] :: str_acc
) (ix
+ 1)
153 if str_acc
= [] then fail_parse "double '.'";
154 let path_el = Rec
(implode (List.rev str_acc
)) in
155 parse_dot (path_el :: acc
) [] (ix
+ 1)
156 | c
-> parse_dot acc
(c
:: str_acc
) (ix
+ 1) in
157 parse_dot acc
[] dot_ix1
158 | c
-> fail_parse (sprintf
"'.' expected; got '%c'" c
) in
161 let get_subst path
str sexp
=
164 | Some
path, _
-> path
165 | None
, Some
str -> parse str
166 | None
, None
-> [] in
169 let get ?
path ?
str sexp
= snd
(get_subst path str sexp
)
171 let replace ?
path ?
str sexp ~
subst =
172 let subst_fun, _
= get_subst path str sexp
in
175 let replace_no_path ~
str sexp ~
subst = replace ~
str sexp ~
subst