1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
5pub enum StringSegment {
6 Literal(String),
7 Expression(String, usize, usize),
9}
10
11impl fmt::Display for StringSegment {
12 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13 match self {
14 StringSegment::Literal(s) => write!(f, "{s}"),
15 StringSegment::Expression(e, _, _) => write!(f, "${{{e}}}"),
16 }
17 }
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
22pub struct Span {
23 pub start: usize,
25 pub end: usize,
27 pub line: usize,
29 pub column: usize,
31 pub end_line: usize,
33}
34
35impl Span {
36 pub fn with_offsets(start: usize, end: usize, line: usize, column: usize) -> Self {
37 Self {
38 start,
39 end,
40 line,
41 column,
42 end_line: line,
43 }
44 }
45
46 pub fn merge(a: Span, b: Span) -> Span {
48 Span {
49 start: a.start,
50 end: b.end,
51 line: a.line,
52 column: a.column,
53 end_line: b.end_line,
54 }
55 }
56
57 pub fn dummy() -> Self {
59 Self {
60 start: 0,
61 end: 0,
62 line: 0,
63 column: 0,
64 end_line: 0,
65 }
66 }
67}
68
69impl fmt::Display for Span {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}:{}", self.line, self.column)
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct FixEdit {
78 pub span: Span,
80 pub replacement: String,
82}
83
84impl FixEdit {
85 pub fn dedupe_overlapping(edits: &[FixEdit]) -> Vec<FixEdit> {
92 let mut sorted = edits.to_vec();
93 sorted.sort_by_key(|edit| std::cmp::Reverse(edit.span.start));
94 let mut accepted: Vec<FixEdit> = Vec::new();
95 for edit in sorted {
96 let overlaps = accepted
97 .iter()
98 .any(|prev| edit.span.start < prev.span.end && edit.span.end > prev.span.start);
99 if !overlaps {
100 accepted.push(edit);
101 }
102 }
103 accepted
104 }
105
106 pub fn apply_all(source: &str, edits: &[FixEdit]) -> String {
111 let mut out = source.to_string();
112 for edit in Self::dedupe_overlapping(edits) {
113 let before = &out[..edit.span.start];
114 let after = &out[edit.span.end..];
115 out = format!("{before}{}{after}", edit.replacement);
116 }
117 out
118 }
119}
120
121#[cfg(test)]
122mod fix_edit_tests {
123 use super::*;
124
125 fn edit(start: usize, end: usize, replacement: &str) -> FixEdit {
126 FixEdit {
127 span: Span::with_offsets(start, end, 1, start + 1),
128 replacement: replacement.to_string(),
129 }
130 }
131
132 #[test]
133 fn apply_all_splices_right_to_left() {
134 let out = FixEdit::apply_all("0123456789", &[edit(2, 4, "AB"), edit(6, 8, "CD")]);
136 assert_eq!(out, "01AB45CD89");
137 }
138
139 #[test]
140 fn apply_all_drops_overlapping_edits_descending_start_wins() {
141 let out = FixEdit::apply_all("0123456789", &[edit(2, 6, "XXXX"), edit(4, 8, "YYYY")]);
144 assert_eq!(out, "0123YYYY89");
145 assert_eq!(
146 FixEdit::dedupe_overlapping(&[edit(2, 6, "x"), edit(4, 8, "y")]).len(),
147 1
148 );
149 }
150}
151
152pub const KEYWORDS: &[&str] = &[
157 "ask_user",
158 "break",
159 "catch",
160 "const",
161 "continue",
162 "deadline",
163 "defer",
164 "dual_control",
165 "else",
166 "emit",
167 "enum",
168 "escalate_to",
169 "eval_pack",
170 "exclusive",
171 "extends",
172 "false",
173 "finally",
174 "fn",
175 "for",
176 "from",
177 "guard",
178 "if",
179 "impl",
180 "import",
181 "in",
182 "interface",
183 "let",
184 "match",
185 "mutex",
186 "nil",
187 "override",
188 "parallel",
189 "pipeline",
190 "pub",
191 "request_approval",
192 "require",
193 "retry",
194 "return",
195 "select",
196 "skill",
197 "spawn",
198 "struct",
199 "throw",
200 "throws",
201 "to",
202 "tool",
203 "true",
204 "try",
205 "type",
206 "var",
207 "while",
208 "yield",
209];
210
211#[derive(Debug, Clone, PartialEq)]
213pub enum TokenKind {
214 Pipeline,
215 Extends,
216 Override,
217 Let,
218 Const,
219 Var,
220 If,
221 Else,
222 For,
223 In,
224 Match,
225 Retry,
226 Parallel,
227 Return,
228 Import,
229 True,
230 False,
231 Nil,
232 Try,
233 Catch,
234 Throw,
235 Throws,
238 Finally,
239 Fn,
240 Spawn,
241 While,
242 TypeKw,
243 Enum,
244 EvalPack,
245 Struct,
246 Interface,
247 Emit,
248 Pub,
249 From,
250 To,
251 Tool,
252 Exclusive,
253 Guard,
254 Require,
255 Deadline,
256 Defer,
257 Yield,
258 Mutex,
259 Break,
260 Continue,
261 Select,
262 Impl,
263 Skill,
264 RequestApproval,
266 DualControl,
268 AskUser,
270 EscalateTo,
272
273 Identifier(String),
274 StringLiteral(String),
275 InterpolatedString(Vec<StringSegment>),
276 RawStringLiteral(String),
278 IntLiteral(i64),
279 FloatLiteral(f64),
280 DurationLiteral(u64),
282
283 Eq, Neq, And, Or, Pipe, NilCoal, Pow, QuestionDot, Arrow, Lte, Gte, PlusAssign, MinusAssign, StarAssign, SlashAssign, PercentAssign, Assign, Not, Dot, Plus, Minus, Star, Slash, Percent, Lt, Gt, Question, Bar, Amp, LBrace, RBrace, LParen, RParen, LBracket, RBracket, Comma, Colon, Semicolon, At, LineComment {
326 text: String,
327 is_doc: bool,
328 }, BlockComment {
330 text: String,
331 is_doc: bool,
332 }, Newline,
335 Eof,
336}
337
338impl fmt::Display for TokenKind {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 match self {
341 TokenKind::Pipeline => write!(f, "pipeline"),
342 TokenKind::Extends => write!(f, "extends"),
343 TokenKind::Override => write!(f, "override"),
344 TokenKind::Let => write!(f, "let"),
345 TokenKind::Const => write!(f, "const"),
346 TokenKind::Var => write!(f, "var"),
347 TokenKind::If => write!(f, "if"),
348 TokenKind::Else => write!(f, "else"),
349 TokenKind::For => write!(f, "for"),
350 TokenKind::In => write!(f, "in"),
351 TokenKind::Match => write!(f, "match"),
352 TokenKind::Retry => write!(f, "retry"),
353 TokenKind::Parallel => write!(f, "parallel"),
354 TokenKind::Return => write!(f, "return"),
355 TokenKind::Import => write!(f, "import"),
356 TokenKind::True => write!(f, "true"),
357 TokenKind::False => write!(f, "false"),
358 TokenKind::Nil => write!(f, "nil"),
359 TokenKind::Try => write!(f, "try"),
360 TokenKind::Catch => write!(f, "catch"),
361 TokenKind::Throw => write!(f, "throw"),
362 TokenKind::Throws => write!(f, "throws"),
363 TokenKind::Finally => write!(f, "finally"),
364 TokenKind::Fn => write!(f, "fn"),
365 TokenKind::Spawn => write!(f, "spawn"),
366 TokenKind::While => write!(f, "while"),
367 TokenKind::TypeKw => write!(f, "type"),
368 TokenKind::Enum => write!(f, "enum"),
369 TokenKind::EvalPack => write!(f, "eval_pack"),
370 TokenKind::Struct => write!(f, "struct"),
371 TokenKind::Interface => write!(f, "interface"),
372 TokenKind::Emit => write!(f, "emit"),
373 TokenKind::Pub => write!(f, "pub"),
374 TokenKind::From => write!(f, "from"),
375 TokenKind::To => write!(f, "to"),
376 TokenKind::Tool => write!(f, "tool"),
377 TokenKind::Exclusive => write!(f, "exclusive"),
378 TokenKind::Guard => write!(f, "guard"),
379 TokenKind::Require => write!(f, "require"),
380 TokenKind::Deadline => write!(f, "deadline"),
381 TokenKind::Defer => write!(f, "defer"),
382 TokenKind::Yield => write!(f, "yield"),
383 TokenKind::Mutex => write!(f, "mutex"),
384 TokenKind::Break => write!(f, "break"),
385 TokenKind::Continue => write!(f, "continue"),
386 TokenKind::Select => write!(f, "select"),
387 TokenKind::Impl => write!(f, "impl"),
388 TokenKind::Skill => write!(f, "skill"),
389 TokenKind::RequestApproval => write!(f, "request_approval"),
390 TokenKind::DualControl => write!(f, "dual_control"),
391 TokenKind::AskUser => write!(f, "ask_user"),
392 TokenKind::EscalateTo => write!(f, "escalate_to"),
393 TokenKind::Identifier(s) => write!(f, "id({s})"),
394 TokenKind::StringLiteral(s) => write!(f, "str({s})"),
395 TokenKind::InterpolatedString(_) => write!(f, "istr(...)"),
396 TokenKind::RawStringLiteral(s) => write!(f, "rstr({s})"),
397 TokenKind::IntLiteral(n) => write!(f, "int({n})"),
398 TokenKind::FloatLiteral(n) => write!(f, "float({n})"),
399 TokenKind::DurationLiteral(ms) => write!(f, "duration({ms}ms)"),
400 TokenKind::Eq => write!(f, "=="),
401 TokenKind::Neq => write!(f, "!="),
402 TokenKind::And => write!(f, "&&"),
403 TokenKind::Or => write!(f, "||"),
404 TokenKind::Pipe => write!(f, "|>"),
405 TokenKind::NilCoal => write!(f, "??"),
406 TokenKind::Pow => write!(f, "**"),
407 TokenKind::QuestionDot => write!(f, "?."),
408 TokenKind::Arrow => write!(f, "->"),
409 TokenKind::Lte => write!(f, "<="),
410 TokenKind::Gte => write!(f, ">="),
411 TokenKind::PlusAssign => write!(f, "+="),
412 TokenKind::MinusAssign => write!(f, "-="),
413 TokenKind::StarAssign => write!(f, "*="),
414 TokenKind::SlashAssign => write!(f, "/="),
415 TokenKind::PercentAssign => write!(f, "%="),
416 TokenKind::Assign => write!(f, "="),
417 TokenKind::Not => write!(f, "!"),
418 TokenKind::Dot => write!(f, "."),
419 TokenKind::Plus => write!(f, "+"),
420 TokenKind::Minus => write!(f, "-"),
421 TokenKind::Star => write!(f, "*"),
422 TokenKind::Slash => write!(f, "/"),
423 TokenKind::Percent => write!(f, "%"),
424 TokenKind::Lt => write!(f, "<"),
425 TokenKind::Gt => write!(f, ">"),
426 TokenKind::Question => write!(f, "?"),
427 TokenKind::Bar => write!(f, "|"),
428 TokenKind::Amp => write!(f, "&"),
429 TokenKind::LBrace => write!(f, "{{"),
430 TokenKind::RBrace => write!(f, "}}"),
431 TokenKind::LParen => write!(f, "("),
432 TokenKind::RParen => write!(f, ")"),
433 TokenKind::LBracket => write!(f, "["),
434 TokenKind::RBracket => write!(f, "]"),
435 TokenKind::Comma => write!(f, ","),
436 TokenKind::Colon => write!(f, ":"),
437 TokenKind::Semicolon => write!(f, ";"),
438 TokenKind::At => write!(f, "@"),
439 TokenKind::LineComment { text, is_doc } => {
440 let prefix = if *is_doc { "///" } else { "//" };
441 write!(f, "{prefix} {text}")
442 }
443 TokenKind::BlockComment { text, is_doc } => {
444 let prefix = if *is_doc { "/**" } else { "/*" };
445 write!(f, "{prefix} {text} */")
446 }
447 TokenKind::Newline => write!(f, "\\n"),
448 TokenKind::Eof => write!(f, "EOF"),
449 }
450 }
451}
452
453#[derive(Debug, Clone, PartialEq)]
455pub struct Token {
456 pub kind: TokenKind,
457 pub span: Span,
458}
459
460impl Token {
461 pub fn with_span(kind: TokenKind, span: Span) -> Self {
462 Self { kind, span }
463 }
464}