Skip to main content

ezno_parser/statements/
switch_statement.rs

1use iterator_endiate::EndiateIteratorExt;
2use source_map::Span;
3use tokenizer_lib::{sized_tokens::TokenEnd, Token};
4use visitable_derive::Visitable;
5
6use crate::{
7	ast::MultipleExpression, derive_ASTNode, errors::parse_lexing_error,
8	throw_unexpected_token_with_token, ASTNode, Expression, ParseOptions, StatementOrDeclaration,
9	TSXKeyword, TSXToken,
10};
11
12#[apply(derive_ASTNode)]
13#[derive(Debug, PartialEq, Clone, Visitable, get_field_by_type::GetFieldByType)]
14#[get_field_by_type_target(Span)]
15pub struct SwitchStatement {
16	pub case: MultipleExpression,
17	pub branches: Vec<SwitchBranch>,
18	pub position: Span,
19}
20
21#[derive(Debug, PartialEq, Clone, Visitable)]
22#[apply(derive_ASTNode)]
23pub enum SwitchBranch {
24	Default(Vec<StatementOrDeclaration>),
25	Case(Expression, Vec<StatementOrDeclaration>),
26}
27
28impl ASTNode for SwitchStatement {
29	fn get_position(&self) -> Span {
30		self.position
31	}
32
33	fn from_reader(
34		reader: &mut impl tokenizer_lib::TokenReader<crate::TSXToken, crate::TokenStart>,
35		state: &mut crate::ParsingState,
36		options: &ParseOptions,
37	) -> Result<Self, crate::ParseError> {
38		let start = state.expect_keyword(reader, TSXKeyword::Switch)?;
39
40		reader.expect_next(crate::TSXToken::OpenParentheses)?;
41		let case = MultipleExpression::from_reader(reader, state, options)?;
42		reader.expect_next(crate::TSXToken::CloseParentheses)?;
43		reader.expect_next(crate::TSXToken::OpenBrace)?;
44
45		let mut branches = Vec::new();
46
47		// TODO not great has this works
48		let close_brace_pos: TokenEnd;
49
50		loop {
51			let case: Option<Expression> = match reader.next().ok_or_else(parse_lexing_error)? {
52				Token(TSXToken::Keyword(TSXKeyword::Default), _) => {
53					reader.expect_next(TSXToken::Colon)?;
54					None
55				}
56				Token(TSXToken::Keyword(TSXKeyword::Case), _) => {
57					let case = Expression::from_reader(reader, state, options)?;
58					reader.expect_next(TSXToken::Colon)?;
59					Some(case)
60				}
61				Token(TSXToken::CloseBrace, pos) => {
62					close_brace_pos = TokenEnd::new(pos.0 + 1);
63					break;
64				}
65				token => {
66					return throw_unexpected_token_with_token(
67						token,
68						&[
69							TSXToken::Keyword(TSXKeyword::Default),
70							TSXToken::Keyword(TSXKeyword::Case),
71							TSXToken::CloseBrace,
72						],
73					);
74				}
75			};
76
77			// This is a modified form of Block::from_reader where `TSXKeyword::Case` and
78			// `TSXKeyword::Default` are delimiters
79			let mut items = Vec::new();
80			loop {
81				if let Some(Token(
82					TSXToken::Keyword(TSXKeyword::Case | TSXKeyword::Default)
83					| TSXToken::CloseBrace,
84					_,
85				)) = reader.peek()
86				{
87					break;
88				}
89				let value = StatementOrDeclaration::from_reader(reader, state, options)?;
90				if value.requires_semi_colon() {
91					let _ = crate::expect_semi_colon(
92						reader,
93						&state.line_starts,
94						value.get_position().end,
95						options,
96					)?;
97				}
98				// Could skip over semi colons regardless. But they are technically empty statements 🤷‍♂️
99				items.push(value);
100			}
101			if let Some(case) = case {
102				branches.push(SwitchBranch::Case(case, items));
103			} else {
104				branches.push(SwitchBranch::Default(items));
105			}
106		}
107		Ok(Self { case, branches, position: start.union(close_brace_pos) })
108	}
109
110	fn to_string_from_buffer<T: source_map::ToString>(
111		&self,
112		buf: &mut T,
113		options: &crate::ToStringOptions,
114		local: crate::LocalToStringInformation,
115	) {
116		buf.push_str("switch");
117		options.push_gap_optionally(buf);
118		buf.push('(');
119		self.case.to_string_from_buffer(buf, options, local);
120		buf.push(')');
121		options.push_gap_optionally(buf);
122		buf.push('{');
123		for branch in &self.branches {
124			if options.pretty {
125				buf.push_new_line();
126				options.add_indent(local.depth + 1, buf);
127			}
128			let local = local.next_level();
129			match branch {
130				SwitchBranch::Default(statements) => {
131					buf.push_str("default:");
132					for (at_end, stmt) in statements.iter().endiate() {
133						if options.pretty {
134							buf.push_new_line();
135							options.add_indent(local.depth + 1, buf);
136						}
137						stmt.to_string_from_buffer(buf, options, local.next_level());
138						if stmt.requires_semi_colon() {
139							buf.push(';');
140						}
141						if options.pretty && !at_end {
142							buf.push_new_line();
143						}
144					}
145				}
146				SwitchBranch::Case(case, statements) => {
147					buf.push_str("case ");
148					case.to_string_from_buffer(buf, options, local);
149					buf.push(':');
150					for (at_end, stmt) in statements.iter().endiate() {
151						if options.pretty {
152							buf.push_new_line();
153							options.add_indent(local.depth + 1, buf);
154						}
155						stmt.to_string_from_buffer(buf, options, local.next_level());
156						if stmt.requires_semi_colon() {
157							buf.push(';');
158						}
159						if options.pretty && !at_end {
160							buf.push_new_line();
161						}
162					}
163				}
164			}
165		}
166		if options.pretty {
167			buf.push_new_line();
168			options.add_indent(local.depth, buf);
169		}
170		buf.push('}');
171	}
172}