compose_syntax/ast/
atomics.rs1use crate::ast::macros::node;
2use crate::ast::{Expr, Statement};
3use crate::SyntaxNode;
4use ecow::EcoString;
5use std::ops::Deref;
6use unscanny::Scanner;
7
8node! {
9 struct Ident
10}
11
12impl<'a> Ident<'a> {
13 pub fn get(self) -> &'a EcoString {
14 self.0.text()
15 }
16
17 pub fn as_str(self) -> &'a str {
18 self.get()
19 }
20}
21
22impl Deref for Ident<'_> {
23 type Target = str;
24
25 fn deref(&self) -> &Self::Target {
28 self.as_str()
29 }
30}
31
32node! {
33 struct Bool
35}
36
37impl Bool<'_> {
38 pub fn get(self) -> bool {
40 self.0.text() == "true"
41 }
42}
43
44node! {
45 struct Int
46}
47
48impl Int<'_> {
49 pub fn get(self) -> i64 {
50 self.0.text().parse().unwrap()
51 }
52}
53
54node! {
55 struct Unit
56}
57
58node! {
59 struct CodeBlock
60}
61
62impl<'a> CodeBlock<'a> {
63 pub fn statements(self) -> impl DoubleEndedIterator<Item = Statement<'a>> {
64 self.0.children().filter_map(SyntaxNode::cast)
65 }
66}
67
68node! {
69 struct Str
70}
71
72impl<'a> Str<'a> {
73 pub fn get(self) -> EcoString {
75 let text = self.0.text();
76 let unquoted = &text[1..text.len() - 1];
77 if !unquoted.contains('\\') {
78 return unquoted.into();
79 }
80
81 let mut out = EcoString::with_capacity(unquoted.len());
82 let mut s = Scanner::new(unquoted);
83
84 while let Some(c) = s.eat() {
85 if c != '\\' {
86 out.push(c);
87 continue;
88 }
89
90 let start = s.locate(-1);
91 match s.eat() {
92 Some('n') => out.push('\n'),
93 Some('r') => out.push('\r'),
94 Some('"') => out.push('"'),
95 Some('t') => out.push('\t'),
96 Some('\\') => out.push('\\'),
97 Some('u') if s.eat_if('{') => {
98 let sequence = s.eat_while(char::is_ascii_hexdigit);
99 s.eat_if('}');
100
101 match u32::from_str_radix(sequence, 16)
102 .ok()
103 .and_then(std::char::from_u32)
104 {
105 Some(c) => out.push(c),
106 None => out.push_str(s.from(start)),
107 }
108 }
109 _ => out.push_str(s.from(start)),
110 }
111 }
112
113 out
114 }
115}
116
117node! {
118 struct Array
119}
120
121impl<'a> Array<'a> {
122 pub fn elements(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
123 self.0.children().filter_map(SyntaxNode::cast)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::assert_ast;
131
132 #[test]
133 fn parse_ident() {
134 assert_ast!(
135 "foo",
136 ident as Ident {
137 assert_eq!(ident.get(), "foo");
138 }
139 )
140 }
141
142 #[test]
143 fn parse_bool() {
144 assert_ast!(
145 "true",
146 bool as Bool {
147 assert_eq!(bool.get(), true);
148 }
149 )
150 }
151
152 #[test]
153 fn parse_int() {
154 assert_ast!(
155 "123",
156 int as Int {
157 assert_eq!(int.get(), 123);
158 }
159 )
160 }
161
162 #[test]
163 fn parse_str() {
164 assert_ast!(
165 "\"foo\"",
166 str as Str {
167 assert_eq!(str.get(), "foo");
168 }
169 )
170 }
171
172 #[test]
173 fn parse_array() {
174 assert_ast!(
175 "[1, 2, 3]",
176 array as Array {
177 array.elements() => [
178 int as Int { assert_eq!(int.get(), 1); }
179 int as Int { assert_eq!(int.get(), 2); }
180 int as Int { assert_eq!(int.get(), 3); }
181 ]
182 }
183 )
184 }
185
186 #[test]
187 fn parse_code_block() {
188 assert_ast!(
189 "{ 1; 2; 3; }",
190 code_block as CodeBlock {
191 code_block.statements() => [
192 int as Int { assert_eq!(int.get(), 1); }
193 int as Int { assert_eq!(int.get(), 2); }
194 int as Int { assert_eq!(int.get(), 3); }
195 ]
196 }
197 )
198 }
199
200 #[test]
201 fn parse_unit() {
202 assert_ast!(
203 "()",
204 _unit as Unit {}
205 )
206 }
207}