1use std::collections::HashMap;
14
15use super::aborts::rust_macro_message;
16use super::diagnostics::{Result, TranslationError, type_error, unsupported};
17use super::lexer::{Token, TokenCursor, TokenKind, describe, tokenize};
18use super::surface::{
19 BinaryOp, Flavor, Rounding, SComparison, SCtor, SData, SEffect, SExpr, SField, SFn, SItem,
20 SMain, SModule, SNode, SParam, SPattern, SPatternNode, SProgram, SProp, SPropNode, SRow,
21 ShowStyle, UnaryOp,
22};
23use super::types::{BOOL, STRING, Type, UNIT, rust_fixed_type};
24use super::{Language, Span};
25
26mod expressions;
27mod items;
28
29const ACCEPTED_DERIVES: [&str; 8] = [
30 "Clone",
31 "Copy",
32 "Debug",
33 "PartialEq",
34 "Eq",
35 "Hash",
36 "PartialOrd",
37 "Ord",
38];
39const ACCEPTED_LINTS: [&str; 6] = ["allow", "warn", "deny", "expect", "must_use", "inline"];
40const RESERVED_ITEMS: [&str; 10] = [
41 "impl",
42 "trait",
43 "struct",
44 "union",
45 "static",
46 "type",
47 "extern",
48 "macro_rules",
49 "async",
50 "unsafe",
51];
52const UNSUPPORTED_TYPES: [&str; 11] = [
53 "str", "char", "f32", "f64", "Vec", "Option", "Result", "Rc", "Arc", "HashMap", "Self",
54];
55
56fn comparison_op(value: &str) -> Option<BinaryOp> {
57 Some(match value {
58 "==" => BinaryOp::Eq,
59 "!=" => BinaryOp::Ne,
60 "<" => BinaryOp::Lt,
61 "<=" => BinaryOp::Le,
62 ">" => BinaryOp::Gt,
63 ">=" => BinaryOp::Ge,
64 _ => return None,
65 })
66}
67
68fn additive_op(value: &str) -> Option<BinaryOp> {
69 Some(match value {
70 "+" => BinaryOp::Add,
71 "-" => BinaryOp::Sub,
72 _ => return None,
73 })
74}
75
76fn multiplicative_op(value: &str) -> Option<BinaryOp> {
77 Some(match value {
78 "*" => BinaryOp::Mul,
79 "/" => BinaryOp::Div,
80 "%" => BinaryOp::Rem,
81 _ => return None,
82 })
83}
84
85pub fn parse_rust(source: &str) -> Result<SProgram> {
90 let tokens = tokenize(source, Language::Rust)?.tokens;
91 RustParser::new(tokens).file()
92}
93
94#[derive(Clone, Copy)]
96enum ItemsEnd {
97 File,
98 Module,
99}
100
101#[derive(Clone, Copy, Default)]
103struct Options {
104 statement: bool,
106 no_struct: bool,
108}
109
110struct Alias {
112 name: String,
113 value: SExpr,
114}
115
116struct Binding {
118 name: String,
119 ty: Option<Type>,
120 value: SExpr,
121 span: Option<Span>,
122}
123
124struct RustParser {
125 cursor: TokenCursor,
126 aliases: HashMap<String, HashMap<String, Vec<String>>>,
128 module_path: Vec<String>,
129 main: Option<SMain>,
130}
131
132impl RustParser {
133 fn new(tokens: Vec<Token>) -> Self {
134 Self {
135 cursor: TokenCursor::new(tokens, Language::Rust),
136 aliases: HashMap::new(),
137 module_path: Vec::new(),
138 main: None,
139 }
140 }
141
142 fn peek(&self) -> Token {
143 self.cursor.peek().clone()
144 }
145
146 fn fail(message: &str, token: &Token) -> TranslationError {
147 TranslationError::syntax(
148 format!("{message} but found {}", describe(token)),
149 Some(token.span()),
150 )
151 }
152}
153
154fn format(args: Vec<SExpr>, token: &Token) -> Result<SExpr> {
158 let mut args = args.into_iter();
159 let template = args.next();
160 let values: Vec<SExpr> = args.collect();
161 let Some(SExpr {
162 node: SNode::Str { value: source },
163 span: template_span,
164 ..
165 }) = template
166 else {
167 return Err(unsupported(
168 &format!("{}!", token.value),
169 "the format string must be a literal",
170 span(token, token),
171 ));
172 };
173 let chars: Vec<char> = source.chars().collect();
174 let mut pieces: Vec<SExpr> = Vec::new();
175 let mut text = String::new();
176 let mut next = 0;
177 let mut index = 0;
178 while index < chars.len() {
179 let char = chars[index];
180 let following = chars.get(index + 1).copied();
181 if char == '{' && following == Some('{') {
182 text.push('{');
183 index += 2;
184 continue;
185 }
186 if char == '}' && following == Some('}') {
187 text.push('}');
188 index += 2;
189 continue;
190 }
191 if char == '}' {
192 return Err(TranslationError::syntax(
193 "unmatched } in format string",
194 template_span,
195 ));
196 }
197 if char != '{' {
198 text.push(char);
199 index += 1;
200 continue;
201 }
202 let Some(end) = chars[index..]
203 .iter()
204 .position(|&candidate| candidate == '}')
205 .map(|offset| index + offset)
206 else {
207 return Err(TranslationError::syntax(
208 "unclosed { in format string",
209 template_span,
210 ));
211 };
212 let spec: String = chars[index + 1..end].iter().collect();
213 if spec.contains(':') {
214 return Err(unsupported(
215 &format!("format spec {{{spec}}}"),
216 "only plain {} Display formatting is portable",
217 template_span,
218 ));
219 }
220 let value = if spec.is_empty() {
221 let value = values.get(next).cloned();
222 next += 1;
223 value.ok_or_else(|| {
224 TranslationError::syntax("format string has more {} than arguments", template_span)
225 })?
226 } else if spec.bytes().all(|byte| byte.is_ascii_digit()) {
227 let position = spec.parse::<usize>().ok();
229 let value = position.and_then(|position| values.get(position).cloned());
230 let (Some(position), Some(value)) = (position, value) else {
231 return Err(TranslationError::syntax(
232 format!("format argument {spec} is missing"),
233 template_span,
234 ));
235 };
236 next = next.max(position + 1);
237 value
238 } else if is_identifier(&spec) {
239 SExpr::new(SNode::Name { path: vec![spec] }, template_span)
240 } else {
241 return Err(unsupported(
242 &format!("format argument {{{spec}}}"),
243 "outside the portable core",
244 template_span,
245 ));
246 };
247 if !text.is_empty() {
248 pieces.push(SExpr::new(
249 SNode::Str {
250 value: std::mem::take(&mut text),
251 },
252 template_span,
253 ));
254 }
255 let value_span = value.span;
256 pieces.push(SExpr::new(
257 SNode::Show {
258 arg: Box::new(value),
259 style: ShowStyle::Rust,
260 },
261 value_span,
262 ));
263 index = end + 1;
264 }
265 if next < values.len() {
266 return Err(TranslationError::syntax(
267 "format string has fewer {} than arguments",
268 template_span,
269 ));
270 }
271 if !text.is_empty() || pieces.is_empty() {
272 pieces.push(SExpr::new(SNode::Str { value: text }, template_span));
273 }
274 let mut pieces = pieces.into_iter();
275 let first = pieces
276 .next()
277 .unwrap_or_else(|| unreachable!("a piece was pushed"));
278 Ok(pieces.fold(first, |left, right| {
279 SExpr::new(
280 SNode::Binary {
281 op: BinaryOp::Concat,
282 left: Box::new(left),
283 right: Box::new(right),
284 rounding: None,
285 },
286 template_span,
287 )
288 }))
289}
290
291fn is_identifier(text: &str) -> bool {
293 let mut bytes = text.bytes();
294 bytes
295 .next()
296 .is_some_and(|first| first.is_ascii_alphabetic() || first == b'_')
297 && bytes.all(|byte| byte.is_ascii_alphanumeric() || byte == b'_')
298}
299
300fn method_call(
301 receiver: SExpr,
302 method: &Token,
303 args: Vec<SExpr>,
304 range: Option<Span>,
305) -> Result<SExpr> {
306 let expect = |count: usize| {
307 if args.len() == count {
308 Ok(())
309 } else {
310 Err(type_error(
311 format!(
312 "{} expects {count} arguments but got {}",
313 method.value,
314 args.len()
315 ),
316 range,
317 ))
318 }
319 };
320 match method.value.as_str() {
321 "clone" | "to_owned" => {
322 expect(0)?;
323 Ok(receiver)
324 }
325 "to_string" => {
326 expect(0)?;
327 Ok(SExpr::new(
328 SNode::ToString {
329 arg: Box::new(receiver),
330 },
331 range,
332 ))
333 }
334 "div_euclid" | "rem_euclid" => {
335 expect(1)?;
336 let op = if method.value == "div_euclid" {
337 BinaryOp::Div
338 } else {
339 BinaryOp::Rem
340 };
341 let right = args
342 .into_iter()
343 .next()
344 .unwrap_or_else(|| unreachable!("one argument"));
345 Ok(SExpr::new(
346 SNode::Binary {
347 op,
348 left: Box::new(receiver),
349 right: Box::new(right),
350 rounding: Some(Rounding::Euclid),
351 },
352 range,
353 ))
354 }
355 _ => Err(unsupported(
356 &format!("method {}", method.value),
357 "outside the portable core",
358 range,
359 )),
360 }
361}
362
363fn absolute(segments: &[String], path: &[String], range: Option<Span>) -> Result<Vec<String>> {
365 let crate_root = || vec!["crate".to_owned()];
366 if segments[0] == "crate" {
367 return Ok(segments.to_vec());
368 }
369 if segments[0] == "self" {
370 let mut result = crate_root();
371 result.extend_from_slice(path);
372 result.extend_from_slice(&segments[1..]);
373 return Ok(result);
374 }
375 let up = segments
376 .iter()
377 .take_while(|segment| *segment == "super")
378 .count();
379 if up > path.len() {
380 return Err(type_error("super beyond crate root", range));
381 }
382 let mut result = crate_root();
383 result.extend_from_slice(&path[..path.len() - up]);
384 result.extend_from_slice(&segments[up..]);
385 Ok(result)
386}
387
388fn prop_of(expr: SExpr) -> SProp {
389 let node = match expr.node {
390 SNode::Binary {
391 op:
392 op @ (BinaryOp::Eq
393 | BinaryOp::Ne
394 | BinaryOp::Lt
395 | BinaryOp::Le
396 | BinaryOp::Gt
397 | BinaryOp::Ge),
398 left,
399 right,
400 ..
401 } => {
402 let comparison = SComparison {
403 left: *left,
404 right: *right,
405 reference: false,
406 same_value: false,
407 };
408 match op {
409 BinaryOp::Eq => SPropNode::Eq(comparison),
410 BinaryOp::Ne => SPropNode::Ne(comparison),
411 BinaryOp::Lt => SPropNode::Lt(comparison),
412 BinaryOp::Le => SPropNode::Le(comparison),
413 BinaryOp::Gt => SPropNode::Gt(comparison),
414 _ => SPropNode::Ge(comparison),
415 }
416 }
417 SNode::Binary {
418 op: BinaryOp::And,
419 left,
420 right,
421 ..
422 } => SPropNode::And {
423 left: Box::new(prop_of(*left)),
424 right: Box::new(prop_of(*right)),
425 },
426 SNode::Binary {
427 op: BinaryOp::Or,
428 left,
429 right,
430 ..
431 } => SPropNode::Or {
432 left: Box::new(prop_of(*left)),
433 right: Box::new(prop_of(*right)),
434 },
435 SNode::Unary {
436 op: UnaryOp::Not,
437 arg,
438 } => SPropNode::Not {
439 arg: Box::new(prop_of(*arg)),
440 },
441 node => SPropNode::Bool {
442 expr: SExpr { node, ..expr },
443 },
444 };
445 SProp { node, span: None }
446}
447
448fn pattern_value(pattern: &SPattern, range: Option<Span>) -> Result<SExpr> {
450 let node = match &pattern.node {
451 SPatternNode::BindOrCtor { name } => SNode::Name {
452 path: vec![name.clone()],
453 },
454 SPatternNode::NumLit { value, negative } => SNode::Num {
455 value: value.clone(),
456 ty: None,
457 negative: *negative,
458 unit: false,
459 },
460 SPatternNode::BoolLit { value, .. } => SNode::Bool { value: *value },
461 SPatternNode::Ctor { path, args } if args.is_empty() => SNode::Name { path: path.clone() },
462 SPatternNode::Ctor { path, args } => SNode::App {
463 func: Box::new(SExpr::new(SNode::Name { path: path.clone() }, range)),
464 args: args
465 .iter()
466 .map(|arg| pattern_value(arg, range))
467 .collect::<Result<_>>()?,
468 },
469 _ => {
470 return Err(unsupported(
471 "@ binding",
472 "the aliased pattern must bind every field",
473 range,
474 ));
475 }
476 };
477 Ok(SExpr::new(node, range))
478}
479
480const fn is_if_or_match(expr: &SExpr) -> bool {
481 matches!(expr.node, SNode::If { .. } | SNode::Match { .. })
482}
483
484fn operator(token: &Token, table: fn(&str) -> Option<BinaryOp>) -> Option<BinaryOp> {
485 if token.kind == TokenKind::Punct {
486 table(&token.value)
487 } else {
488 None
489 }
490}
491
492fn binary(op: BinaryOp, left: SExpr, right: SExpr, token: &Token) -> SExpr {
493 let range = joined(left.span, right.span, token);
494 SExpr::new(
495 SNode::Binary {
496 op,
497 left: Box::new(left),
498 right: Box::new(right),
499 rounding: None,
500 },
501 range,
502 )
503}
504
505#[allow(clippy::unnecessary_wraps)] fn joined(left: Option<Span>, right: Option<Span>, token: &Token) -> Option<Span> {
507 Some(Span::new(
508 left.map_or(token.start, |left| left.start),
509 right.map_or(token.end, |right| right.end),
510 ))
511}
512
513#[allow(clippy::unnecessary_wraps)] fn span(from: &Token, to: &Token) -> Option<Span> {
515 let to_start = if to.kind == TokenKind::Eof {
516 from.end
517 } else {
518 to.start
519 };
520 Some(Span::new(from.start, from.end.max(to_start)))
521}