lucia_lang/
utils.rs

1//! Utilities for lucia-lang.
2
3use std::fmt::{Debug, Display, Write};
4
5/// Location of token in the code.
6#[derive(Clone, Copy, PartialEq, Eq)]
7pub struct Location {
8    /// The lineno.
9    pub lineno: u32,
10    /// The column.
11    pub column: u32,
12    /// The character offset, counting from 0.
13    pub offset: u32,
14}
15
16impl Debug for Location {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        write!(f, "{}:{}({})", self.lineno, self.column, self.offset)
19    }
20}
21
22impl Display for Location {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        Debug::fmt(self, f)
25    }
26}
27
28/// Escape lucia str.
29///
30/// The exact rules are:
31///
32/// * Tab is escaped as `\t`.
33/// * Carriage return is escaped as `\r`.
34/// * Line feed is escaped as `\n`.
35/// * Backslash is escaped as `\\`.
36/// * Single quote is escaped as `\'`.
37/// * Double quote is escaped as `\"`.
38/// * Any character in the 'printable ASCII' range `0x20` .. `0x7e`
39///   inclusive is not escaped.
40/// * If `ascii_only` is true, all other characters are given
41///   hexadecimal Unicode escapes.
42pub fn escape_str(value: &str, ascii_only: bool) -> String {
43    let mut ans = String::new();
44    for c in value.chars() {
45        match c {
46            '\0' => ans.push_str("\\0"),
47            '\t' => ans.push_str("\\t"),
48            '\r' => ans.push_str("\\r"),
49            '\n' => ans.push_str("\\n"),
50            '\\' => ans.push_str("\\\\"),
51            '"' => ans.push_str("\\\""),
52            '\'' => ans.push_str("\\\'"),
53            '\x20'..='\x7e' if ascii_only => ans.push(c),
54            _ if ascii_only => ans.push_str(&c.escape_default().to_string()),
55            _ => ans.push(c),
56        }
57    }
58    ans
59}
60
61pub(crate) trait Join<Item: Display>: Iterator<Item = Item> {
62    fn join(&mut self, sep: &str) -> String {
63        if let Some(first) = self.next() {
64            let (lb, _) = self.size_hint();
65            let mut result = String::with_capacity(sep.len() * lb);
66            write!(&mut result, "{}", first).unwrap();
67            self.for_each(|i| {
68                result.push_str(sep);
69                write!(&mut result, "{}", i).unwrap();
70            });
71            result
72        } else {
73            String::new()
74        }
75    }
76}
77
78impl<T: ?Sized, Item: Display> Join<Item> for T where T: Iterator<Item = Item> {}