1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! The values exchanged between a session and its pipeline.
use ;
/// The pipeline's verdict on the pending input: finished, or waiting for more.
///
/// The pipeline closure passed to [`Session::feed`](crate::Session::feed)
/// returns a `Status`. `Complete` carries whatever the pipeline produced — a
/// parsed tree, an evaluated value, a list of diagnostics, or a `Result` of
/// those — and the session hands it straight back in
/// [`Feed::Complete`]. Because the check for completeness and the real work
/// are the same call, the pipeline parses each finished entry exactly once.
///
/// Return `Complete` for input that is *wrong* as well as input that is
/// right: a syntax error in the middle of a line will not be fixed by typing
/// more lines. Only input that ended too soon should be `Incomplete`; see
/// [`Input::is_incomplete`](crate::Input::is_incomplete) for the usual test.
///
/// # Examples
///
/// ```
/// use repl_lang::{Feed, Session, Status};
///
/// // A pipeline that treats a trailing backslash as a line continuation.
/// let pipeline = |input: repl_lang::Input<'_>| {
/// if input.text().trim_end().ends_with('\\') {
/// Status::Incomplete
/// } else {
/// Status::Complete(input.text().lines().count())
/// }
/// };
///
/// let mut session = Session::new();
/// assert!(matches!(session.feed("echo one \\", pipeline)?, Feed::Incomplete));
/// assert!(matches!(session.feed("two", pipeline)?, Feed::Complete { value: 2, .. }));
/// # Ok::<(), repl_lang::SessionError>(())
/// ```
/// The outcome of feeding one line to a [`Session`](crate::Session).
///
/// A host's read loop matches on it: evaluate and print on `Complete`, show a
/// continuation prompt on `Incomplete`, and simply prompt again on `Empty`.
///
/// # Examples
///
/// ```
/// use repl_lang::{Feed, Session, Status};
///
/// let mut session = Session::new();
/// let pipeline = |input: repl_lang::Input<'_>| {
/// let open = input.text().matches('{').count();
/// let close = input.text().matches('}').count();
/// if open > close { Status::Incomplete } else { Status::Complete(input.text().len()) }
/// };
///
/// let mut transcript = Vec::new();
/// for line in ["", "f {", " x", "}"] {
/// let prompt = if session.is_pending() { "... " } else { ">>> " };
/// match session.feed(line, pipeline)? {
/// Feed::Complete { entry, value } => {
/// transcript.push(format!("{prompt}{line} -> entry {} ({value} bytes)", entry.number()));
/// }
/// Feed::Incomplete => transcript.push(format!("{prompt}{line}")),
/// Feed::Empty => transcript.push(prompt.trim_end().to_owned()),
/// }
/// }
/// assert_eq!(transcript, [">>>", ">>> f {", "... x", "... } -> entry 1 (10 bytes)"]);
/// # Ok::<(), repl_lang::SessionError>(())
/// ```
/// A committed entry: its number and where its text lives in the session's
/// source map.
///
/// Returned in [`Feed::Complete`]. The text itself is stored once, in the
/// session; an `Entry` is a small `Copy` handle to it.
///
/// # Examples
///
/// ```
/// use repl_lang::{Feed, Session, Span, Status};
///
/// let mut session = Session::new();
/// session.feed("first", |_| Status::Complete(()))?;
/// let Feed::Complete { entry, .. } = session.feed("second", |_| Status::Complete(()))? else {
/// return Err("the pipeline always completes".into());
/// };
///
/// assert_eq!(entry.number(), 2);
/// assert_eq!(entry.span(), Span::new(6, 13));
/// let source = session.sources().source(entry.id()).ok_or("missing")?;
/// assert_eq!(source.name(), "<repl:2>");
/// assert_eq!(source.text(), "second\n");
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```