1use self::components::{BinaryOp, BitstringItem, UnaryOp};
5use crate::format::{Format, Formatter};
6use crate::items::Type;
7use crate::items::components::{
8 Args, BitstringLike, Either, Element, ListLike, MapLike, Maybe, NonEmptyItems, Params,
9 Parenthesized, RecordLike, TupleLike,
10};
11use crate::items::keywords::FunKeyword;
12use crate::items::symbols::{
13 ColonSymbol, DoubleColonSymbol, RightArrowSymbol, SharpSymbol, TripleDotSymbol,
14 VerticalBarSymbol,
15};
16use crate::items::tokens::{AtomToken, CharToken, IntegerToken, VariableToken};
17use crate::parse::{self, Parse, ResumeParse};
18use crate::span::Span;
19
20pub mod components;
21
22#[derive(Debug, Clone, Span, Parse, Format)]
24pub struct UnionType(NonEmptyItems<NonUnionType, UnionDelimiter>);
25
26impl UnionType {
27 pub(crate) fn items(&self) -> &[NonUnionType] {
28 self.0.items()
29 }
30}
31
32#[derive(Debug, Clone, Span, Parse)]
33struct UnionDelimiter(VerticalBarSymbol);
34
35impl Format for UnionDelimiter {
36 fn format(&self, fmt: &mut Formatter) {
37 fmt.write_space();
38 self.0.format(fmt);
39 }
40}
41
42#[derive(Debug, Clone, Span, Format)]
43pub enum NonUnionType {
44 Base(BaseType),
45 BinaryOp(Box<BinaryOpType>),
46}
47
48impl Parse for NonUnionType {
49 fn parse(ts: &mut parse::TokenStream) -> parse::Result<Self> {
50 let expr: BaseType = ts.parse()?;
51 if ts.peek::<BinaryOp>().is_some() {
52 ts.resume_parse(expr).map(Self::BinaryOp)
53 } else {
54 Ok(Self::Base(expr))
55 }
56 }
57}
58
59#[derive(Debug, Clone, Span, Parse, Format)]
61pub enum BaseType {
62 Mfargs(Box<MfargsType>),
63 List(Box<ListType>),
64 Tuple(Box<TupleType>),
65 Map(Box<MapType>),
66 Record(Box<RecordType>),
67 Bitstring(Box<BitstringType>),
68 Function(Box<FunctionType>),
69 UnaryOp(Box<UnaryOpType>),
70 Parenthesized(Box<Parenthesized<Type>>),
71 Annotated(Box<AnnotatedVariableType>),
72 Literal(LiteralType),
73}
74
75#[derive(Debug, Clone, Span, Parse)]
77pub struct AnnotatedVariableType {
78 variable: VariableToken,
79 colon: DoubleColonSymbol,
80 ty: Type,
81}
82
83impl AnnotatedVariableType {
84 pub fn variable(&self) -> &VariableToken {
85 &self.variable
86 }
87
88 pub fn ty(&self) -> &Type {
89 &self.ty
90 }
91}
92
93impl Format for AnnotatedVariableType {
94 fn format(&self, fmt: &mut Formatter) {
95 self.variable.format(fmt);
96 fmt.write_space();
97 self.colon.format(fmt);
98 fmt.write_space();
99 self.ty.format(fmt);
100 }
101}
102
103#[derive(Debug, Clone, Span, Parse)]
105pub struct BinaryOpType {
106 left: BaseType,
107 op: BinaryOp,
108 right: Type,
109}
110
111impl ResumeParse<BaseType> for BinaryOpType {
112 fn resume_parse(ts: &mut parse::TokenStream, left: BaseType) -> parse::Result<Self> {
113 Ok(Self {
114 left,
115 op: ts.parse()?,
116 right: ts.parse()?,
117 })
118 }
119}
120
121impl Format for BinaryOpType {
122 fn format(&self, fmt: &mut Formatter) {
123 let needs_space = !matches!(self.op, BinaryOp::Range(_));
124
125 self.left.format(fmt);
126 if needs_space {
127 fmt.write_space();
128 }
129 self.op.format(fmt);
130 if needs_space {
131 fmt.write_space();
132 }
133 self.right.format(fmt);
134 }
135}
136
137#[derive(Debug, Clone, Span, Parse)]
139pub struct UnaryOpType {
140 op: UnaryOp,
141 ty: BaseType,
142}
143
144impl Format for UnaryOpType {
145 fn format(&self, fmt: &mut Formatter) {
146 let last = fmt.last_char().unwrap_or('\n');
147 if !matches!(last, '\n' | ' ' | '.') {
148 fmt.write_space();
149 }
150
151 self.op.format(fmt);
152 if matches!(self.op, UnaryOp::Bnot(_)) {
153 fmt.write_space();
154 }
155 self.ty.format(fmt);
156 }
157}
158
159#[derive(Debug, Clone, Span, Parse)]
164pub struct FunctionType {
165 fun: FunKeyword,
166 params_and_return: Parenthesized<Maybe<FunctionParamsAndReturn>>,
167}
168
169impl FunctionType {
170 pub fn params(&self) -> impl Iterator<Item = &Type> {
171 self.params_and_return
172 .get()
173 .get()
174 .and_then(|x| match &x.params {
175 FunctionParams::Any(_) => None,
176 FunctionParams::Params(x) => Some(x.get()),
177 })
178 .into_iter()
179 .flat_map(|x| x.iter())
180 }
181
182 pub fn return_type(&self) -> Option<&Type> {
183 self.params_and_return.get().get().map(|x| &x.ty)
184 }
185}
186
187impl Format for FunctionType {
188 fn format(&self, fmt: &mut Formatter) {
189 self.fun.format(fmt);
190 self.params_and_return.format(fmt);
191 }
192}
193
194#[derive(Debug, Clone, Span, Parse)]
195struct FunctionParamsAndReturn {
196 params: FunctionParams,
197 arrow: RightArrowSymbol,
198 ty: Type,
199}
200
201impl Format for FunctionParamsAndReturn {
202 fn format(&self, fmt: &mut Formatter) {
203 fmt.with_scoped_indent(|fmt| {
204 self.params.format(fmt);
206 fmt.write_space();
207
208 let multiline = fmt.has_newline_until(&self.ty);
210 self.arrow.format(fmt);
211 if multiline {
212 fmt.set_indent(fmt.indent() + 8);
213 fmt.write_newline();
214 } else {
215 fmt.write_space();
216 }
217
218 self.ty.format(fmt);
220 });
221 }
222}
223
224#[derive(Debug, Clone, Span, Parse, Format)]
225enum FunctionParams {
226 Any(Parenthesized<TripleDotSymbol>),
227 Params(Params<Type>),
228}
229
230#[derive(Debug, Clone, Span, Parse, Format)]
232pub enum LiteralType {
233 Atom(AtomToken),
234 Char(CharToken),
235 Integer(IntegerToken),
236 Variable(VariableToken),
237}
238
239#[derive(Debug, Clone, Span, Parse, Format)]
245pub struct MfargsType {
246 module: Maybe<(AtomToken, ColonSymbol)>,
247 name: AtomToken,
248 args: Args<Type>,
249}
250
251impl MfargsType {
252 pub fn module_name(&self) -> Option<&AtomToken> {
253 self.module.get().map(|(name, _)| name)
254 }
255
256 pub fn type_name(&self) -> &AtomToken {
257 &self.name
258 }
259
260 pub fn args(&self) -> &[Type] {
261 self.args.get()
262 }
263}
264
265#[derive(Debug, Clone, Span, Parse, Format)]
269pub struct ListType(ListLike<ListItem>);
270
271impl ListType {
272 pub fn item_type(&self) -> Option<&Type> {
273 self.0.items().first().and_then(|item| match &item.0 {
274 Either::A(x) => Some(x),
275 Either::B(_) => None,
276 })
277 }
278}
279
280#[derive(Debug, Clone, Span, Parse, Format)]
281struct ListItem(Either<Type, TripleDotSymbol>);
282
283impl Element for ListItem {
284 fn is_packable(&self) -> bool {
285 false
286 }
287}
288
289#[derive(Debug, Clone, Span, Parse, Format)]
291pub struct TupleType(TupleLike<TupleItem>);
292
293impl TupleType {
294 pub fn items(&self) -> (Option<&AtomToken>, impl Iterator<Item = &Type>) {
295 let (tag, items) = self.0.items();
296 (tag, items.iter().map(|item| &item.0))
297 }
298}
299
300#[derive(Debug, Clone, Span, Parse, Format, Element)]
301struct TupleItem(Type);
302
303#[derive(Debug, Clone, Span, Parse, Format)]
305pub struct MapType(MapLike<SharpSymbol, Type>);
306
307impl MapType {
308 pub fn items(&self) -> impl Iterator<Item = (&Type, &Type)> {
309 self.0.items()
310 }
311}
312
313#[derive(Debug, Clone, Span, Parse, Format)]
318pub struct RecordType {
319 record: RecordLike<(SharpSymbol, AtomToken), RecordItem>,
320}
321
322impl RecordType {
323 pub fn name(&self) -> &AtomToken {
324 &self.record.prefix().1
325 }
326
327 pub fn fields(&self) -> impl Iterator<Item = (&AtomToken, &Type)> {
328 self.record.fields().map(|item| (&item.name, &item.ty))
329 }
330}
331
332#[derive(Debug, Clone, Span, Parse, Element)]
333struct RecordItem {
334 name: AtomToken,
335 colon: DoubleColonSymbol,
336 ty: Type,
337}
338
339impl Format for RecordItem {
340 fn format(&self, fmt: &mut Formatter) {
341 self.name.format(fmt);
342 fmt.write_space();
343 self.colon.format(fmt);
344 fmt.write_space();
345 self.ty.format(fmt);
346 }
347}
348
349#[derive(Debug, Clone, Span, Parse, Format)]
354pub struct BitstringType(BitstringLike<BitstringItem>);
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
361 fn mfargs_works() {
362 let texts = [
363 "foo()",
364 "foo:bar(A, 1)",
365 indoc::indoc! {"
366 foo:bar(A,
367 BB,
368 baz())"},
369 indoc::indoc! {"
370 foo:bar(A,
371 B,
372 baz(12, 34),
373 qux())"},
374 ];
375 for text in texts {
376 crate::assert_format!(text, Type);
377 }
378 }
379
380 #[test]
381 fn list_works() {
382 let texts = [
383 "[]",
384 "[foo()]",
385 "[10, ...]",
386 indoc::indoc! {"
387 [fooooooooooo(),
388 ...]"},
389 ];
390 for text in texts {
391 crate::assert_format!(text, Type);
392 }
393 }
394
395 #[test]
396 fn tuple_works() {
397 let texts = [
398 "{}",
399 "{foo()}",
400 "{atom, 1}",
401 indoc::indoc! {"
402 {foo(),
403 bar(),
404 [baz()]}"},
405 indoc::indoc! {"
406 {foo(),
407 bar(),
408 [baz(A, B)]}"},
409 indoc::indoc! {"
410 {foo, bar,
411 [baz(A, B)]}"},
412 ];
413 for text in texts {
414 crate::assert_format!(text, Type);
415 }
416 }
417
418 #[test]
419 fn map_works() {
420 let expected = [
421 "#{}",
422 "#{a => b, 1 := 2}",
423 indoc::indoc! {"
424 #{
425 atom() :=
426 integer()
427 }"},
428 indoc::indoc! {"
429 #{
430 atom() := {Aaa,
431 bbb,
432 ccc}
433 }"},
434 indoc::indoc! {"
435 #{
436 a => b,
437 1 := 2,
438 atom() := atom()
439 }"},
440 indoc::indoc! {"
441 #{a => b, 1 := 2, atom() := atom()}"},
442 ];
443 for text in expected {
444 crate::assert_format!(text, Type);
445 }
446 }
447
448 #[test]
449 fn record_works() {
450 let texts = [
451 "#foo{}",
452 indoc::indoc! {"
453 #foo{
454 bar :: integer()
455 }"},
456 indoc::indoc! {"
457 #foo{
458 bar :: b,
459 baz :: 2
460 }"},
461 indoc::indoc! {"
462 #foo{bar :: b, baz :: 2}"},
463 indoc::indoc! {"
464 #foo{
465 bar :: b,
466 baz :: bb()
467 }"},
468 ];
469 for text in texts {
470 crate::assert_format!(text, Type);
471 }
472 }
473
474 #[test]
475 fn function_works() {
476 let texts = [
477 "fun()",
478 "fun(() -> integer())",
479 indoc::indoc! {"
480 fun((...) -> atom())"},
481 indoc::indoc! {"
482 fun((A, b, $c) ->
483 tuple())"},
484 indoc::indoc! {"
485 fun((A,
486 b,
487 $c,
488 {foo()}) ->
489 tuple())"},
490 ];
491 for text in texts {
492 crate::assert_format!(text, Type);
493 }
494 }
495
496 #[test]
497 fn unary_op_works() {
498 let texts = ["-10", "+10", "bnot 100", "- - + +3"];
499 for text in texts {
500 crate::assert_format!(text, Type);
501 }
502 }
503
504 #[test]
505 fn binary_op_works() {
506 let texts = [
507 "-10 + 20 rem 3",
508 indoc::indoc! {"
509 foo |
510 (3 + 10) |
511 -1..+20"},
512 ];
513 for text in texts {
514 crate::assert_format!(text, Type);
515 }
516 }
517
518 #[test]
519 fn bitstring_works() {
520 let texts = [
521 "<<>>",
522 "<<_:10>>",
523 "<<_:_*8>>",
524 "<<_:8, _:_*4>>",
525 indoc::indoc! {"
526 <<_:(1 + 3 + 4),
527 _:_*4>>"},
528 ];
529 for text in texts {
530 crate::assert_format!(text, Type);
531 }
532 }
533
534 #[test]
535 fn annotated_variable_works() {
536 let texts = [
537 "Foo :: atom()",
538 indoc::indoc! {"
539 Foo :: [bar:baz(qux)]"},
540 indoc::indoc! {"
541 Foo :: atom() |
542 integer() |
543 bar"},
544 ];
545 for text in texts {
546 crate::assert_format!(text, Type);
547 }
548 }
549}