1use crate::cst::{Item, ListKind, Node};
2
3pub fn sym(s: &str) -> Node {
5 Node::Atom(s.to_string())
6}
7
8pub fn atom(s: &str) -> Node {
10 Node::Atom(s.to_string())
11}
12
13pub fn string_lit(s: &str) -> Node {
15 Node::Str(escape_string(s))
16}
17
18pub fn quoted_sym(s: &str) -> Node {
20 Node::Prefixed {
21 prefix: "'".to_string(),
22 inner: Box::new(sym(s)),
23 }
24}
25
26pub fn list(children: Vec<Node>) -> Node {
28 let mut items = Vec::with_capacity(children.len() * 2);
29 for (i, child) in children.into_iter().enumerate() {
30 if i > 0 {
31 items.push(Item::Ws(" ".to_string()));
32 }
33 items.push(Item::Node(child));
34 }
35 Node::List {
36 kind: ListKind::Paren,
37 items,
38 }
39}
40
41pub fn escape_string(s: &str) -> String {
43 let mut out = String::with_capacity(s.len() + 2);
44 out.push('"');
45 for c in s.chars() {
46 if c == '"' || c == '\\' {
47 out.push('\\');
48 }
49 out.push(c);
50 }
51 out.push('"');
52 out
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn string_lit_escaping_matches_libguix() {
61 assert_eq!(escape_string("hello"), "\"hello\"");
62 assert_eq!(escape_string("a\"b"), "\"a\\\"b\"");
63 assert_eq!(escape_string("a\\b"), "\"a\\\\b\"");
64 assert_eq!(string_lit("a\"b").to_source(), "\"a\\\"b\"");
65 }
66
67 #[test]
68 fn built_list_prints_flat() {
69 let n = list(vec![sym("name"), quoted_sym("guix")]);
70 assert_eq!(n.to_source(), "(name 'guix)");
71 }
72}