9 sig |
9 sig |
10 type ('a, 'b) source |
10 type ('a, 'b) source |
11 val set_prompt: string -> ('a, 'b) source -> ('a, 'b) source |
11 val set_prompt: string -> ('a, 'b) source -> ('a, 'b) source |
12 val get: ('a, 'b) source -> 'a list * ('a, 'b) source |
12 val get: ('a, 'b) source -> 'a list * ('a, 'b) source |
13 val unget: 'a list * ('a, 'b) source -> ('a, 'b) source |
13 val unget: 'a list * ('a, 'b) source -> ('a, 'b) source |
14 val get_single: ('a, 'b) source -> 'a * ('a, 'b) source |
14 val get_single: ('a, 'b) source -> 'a option * ('a, 'b) source |
15 val exhaust: ('a, 'b) source -> 'a list |
15 val exhaust: ('a, 'b) source -> 'a list |
16 val filter: ('a -> bool) -> ('a, 'b) source -> ('a, ('a, 'b) source) source |
16 val filter: ('a -> bool) -> ('a, 'b) source -> ('a, ('a, 'b) source) source |
17 val of_list: 'a list -> ('a, 'a list) source |
17 val of_list: 'a list -> ('a, 'a list) source |
18 val of_string: string -> (string, string list) source |
18 val of_string: string -> (string, string list) source |
19 val of_stream: TextIO.instream -> TextIO.outstream -> (string, unit) source |
19 val of_stream: TextIO.instream -> TextIO.outstream -> (string, unit) source |
33 |
33 |
34 datatype ('a, 'b) source = |
34 datatype ('a, 'b) source = |
35 Source of |
35 Source of |
36 {buffer: 'a list, |
36 {buffer: 'a list, |
37 info: 'b, |
37 info: 'b, |
38 prompt: string option, |
38 prompt: string, |
39 drain: string -> 'b -> 'a list * 'b}; |
39 drain: string -> 'b -> 'a list * 'b}; |
40 |
40 |
41 fun make_source buffer info prompt drain = |
41 fun make_source buffer info prompt drain = |
42 Source {buffer = buffer, info = info, prompt = prompt, drain = drain}; |
42 Source {buffer = buffer, info = info, prompt = prompt, drain = drain}; |
43 |
43 |
45 (* prompt *) |
45 (* prompt *) |
46 |
46 |
47 val default_prompt = "> "; |
47 val default_prompt = "> "; |
48 |
48 |
49 fun set_prompt prompt (Source {buffer, info, prompt = _, drain}) = |
49 fun set_prompt prompt (Source {buffer, info, prompt = _, drain}) = |
50 make_source buffer info (Some prompt) drain; |
50 make_source buffer info prompt drain; |
51 |
51 |
52 |
52 |
53 (* get / unget *) |
53 (* get / unget *) |
54 |
54 |
55 fun get (Source {buffer = [], info, prompt, drain}) = |
55 fun get (Source {buffer = [], info, prompt, drain}) = |
56 let val (xs, info') = drain (if_none prompt default_prompt) info |
56 let val (xs, info') = drain prompt info |
57 in (xs, make_source [] info' prompt drain) end |
57 in (xs, make_source [] info' prompt drain) end |
58 | get (Source {buffer, info, prompt, drain}) = |
58 | get (Source {buffer, info, prompt, drain}) = |
59 (buffer, make_source [] info prompt drain); |
59 (buffer, make_source [] info prompt drain); |
60 |
60 |
61 fun unget (xs, Source {buffer, info, prompt, drain}) = |
61 fun unget (xs, Source {buffer, info, prompt, drain}) = |
66 |
66 |
67 fun get_prompt prompt src = get (set_prompt prompt src); |
67 fun get_prompt prompt src = get (set_prompt prompt src); |
68 |
68 |
69 fun get_single src = |
69 fun get_single src = |
70 (case get src of |
70 (case get src of |
71 ([], _) => error "Input source exhausted!" |
71 ([], src') => (None, src') |
72 | (x :: xs, src') => (x, unget (xs, src'))); |
72 | (x :: xs, src') => (Some x, unget (xs, src'))); |
73 |
73 |
74 fun exhaust src = |
74 fun exhaust src = |
75 (case get src of |
75 (case get src of |
76 ([], _) => [] |
76 ([], _) => [] |
77 | (xs, src') => xs @ exhaust src'); |
77 | (xs, src') => xs @ exhaust src'); |
87 if null xs then (xs, src') |
87 if null xs then (xs, src') |
88 else if null xs' then drain_filter pred prompt src' |
88 else if null xs' then drain_filter pred prompt src' |
89 else (xs', src') |
89 else (xs', src') |
90 end; |
90 end; |
91 |
91 |
92 fun filter pred src = make_source [] src None (drain_filter pred); |
92 fun filter pred src = make_source [] src default_prompt (drain_filter pred); |
93 |
93 |
94 |
94 |
95 |
95 |
96 (** build sources **) |
96 (** build sources **) |
97 |
97 |
98 (* list source *) |
98 (* list source *) |
99 |
99 |
100 (*limiting the master input buffer considerably improves performance*) |
100 (*limiting the input buffer considerably improves performance*) |
101 val limit = 4000; |
101 val limit = 4000; |
102 |
102 |
103 fun drain_list _ xs = (take (limit, xs), drop (limit, xs)); |
103 fun drain_list _ xs = (take (limit, xs), drop (limit, xs)); |
104 |
104 |
105 fun of_list xs = make_source [] xs None drain_list; |
105 fun of_list xs = make_source [] xs default_prompt drain_list; |
106 fun of_string str = of_list (explode str); |
106 val of_string = of_list o explode; |
107 |
107 |
108 |
108 |
109 (* stream source *) |
109 (* stream source *) |
110 |
110 |
111 fun drain_stream instream outstream prompt () = |
111 fun drain_stream instream outstream prompt () = |
112 (TextIO.output (outstream, prompt); |
112 (TextIO.output (outstream, prompt); |
113 TextIO.flushOut outstream; |
113 TextIO.flushOut outstream; |
114 (explode (TextIO.inputLine instream), ())); |
114 (explode (TextIO.inputLine instream), ())); |
115 |
115 |
116 fun of_stream instream outstream = |
116 fun of_stream instream outstream = |
117 make_source [] () None (drain_stream instream outstream); |
117 make_source [] () default_prompt (drain_stream instream outstream); |
118 |
118 |
119 val tty = of_stream TextIO.stdIn TextIO.stdOut; |
119 val tty = of_stream TextIO.stdIn TextIO.stdOut; |
120 |
120 |
121 |
121 |
122 |
122 |
123 (** compose sources **) |
123 (** compose sources **) |
124 |
124 |
125 (* make state-based *) |
125 fun drain_source source stopper scan prompt src = |
|
126 source prompt get_prompt unget stopper scan src; |
126 |
127 |
127 fun drain_state_source stopper scan prompt state_src = |
128 |
128 Scan.source prompt get_prompt unget stopper scan state_src; |
129 (* state-based *) |
129 |
130 |
130 fun state_source init_state stopper scan src = |
131 fun state_source init_state stopper scan src = |
131 make_source [] (init_state, src) None (drain_state_source stopper scan); |
132 make_source [] (init_state, src) default_prompt (drain_source Scan.source' stopper scan); |
132 |
133 |
133 |
134 |
134 (* not state-based *) |
135 (* non state-based *) |
135 |
|
136 fun drain_source stopper scan prompt src = |
|
137 apsnd snd (Scan.source prompt get_prompt unget stopper (Scan.lift scan) ((), src)); |
|
138 |
136 |
139 fun source stopper scan src = |
137 fun source stopper scan src = |
140 make_source [] src None (drain_source stopper scan); |
138 make_source [] src default_prompt (drain_source Scan.source stopper scan); |
141 |
139 |
142 |
140 |
143 |
141 |
144 (** test source interactively **) |
142 (** test source interactively **) |
145 |
143 |