1use nom::Parser;
16use nom_rule::rule;
17
18use crate::ast::*;
19use crate::parser::common::*;
20use crate::parser::error::Error;
21use crate::parser::error::ErrorKind;
22use crate::parser::expr::*;
23use crate::parser::input::Input;
24use crate::parser::statement::*;
25use crate::parser::token::*;
26
27#[allow(clippy::large_enum_variant)]
28#[derive(Debug, Clone, PartialEq)]
29pub enum ScriptBlockOrStmt {
30 ScriptBlock(ScriptBlock),
31 Statement(Statement),
32}
33
34pub fn script_block_or_stmt(i: Input) -> IResult<ScriptBlockOrStmt> {
35 alt((
36 map(script_block, ScriptBlockOrStmt::ScriptBlock),
37 map(
38 consumed(rule! {
39 #statement
40 }),
41 |(_, stmt)| ScriptBlockOrStmt::Statement(stmt.stmt),
42 ),
43 ))
44 .parse(i)
45}
46
47pub fn script_block(i: Input) -> IResult<ScriptBlock> {
48 map(
49 consumed(rule! {
50 ( DECLARE ~ #semicolon_terminated_list1(declare_item) )?
51 ~ BEGIN
52 ~ #semicolon_terminated_list1(script_stmt)
53 ~ END
54 ~ ";"
55 }),
56 |(span, (declares, _, body, _, _))| {
57 let declares = declares.map(|(_, declare)| declare).unwrap_or_default();
58 ScriptBlock {
59 span: transform_span(span.tokens),
60 declares,
61 body,
62 }
63 },
64 )
65 .parse(i)
66}
67
68pub fn declare_item(i: Input) -> IResult<DeclareItem> {
69 let declare_var = map(declare_var, DeclareItem::Var);
70 let declare_set = map(declare_set, DeclareItem::Set);
71
72 rule!(
73 #declare_var
74 | #declare_set
75 )
76 .parse(i)
77}
78
79pub fn declare_var(i: Input) -> IResult<DeclareVar> {
80 map(
81 consumed(rule! {
82 #ident ~ ( #type_name )? ~ ( ( ":=" | DEFAULT ) ~ ^#expr )?
83 }),
84 |(span, (name, data_type, default))| DeclareVar {
85 span: transform_span(span.tokens),
86 name,
87 data_type,
88 default: default.map(|(_, default)| default),
89 },
90 )
91 .parse(i)
92}
93
94pub fn declare_set(i: Input) -> IResult<DeclareSet> {
95 map(
96 consumed(rule! {
97 #ident ~ RESULTSET ~ ^":=" ~ ^#statement_body
98 }),
99 |(span, (name, _, _, stmt))| DeclareSet {
100 span: transform_span(span.tokens),
101 name,
102 stmt,
103 },
104 )
105 .parse(i)
106}
107
108pub fn declare_cursor(i: Input) -> IResult<DeclareCursor> {
109 map(
110 consumed(rule! {
111 #ident ~ CURSOR ~ ^FOR ~ ^#cursor_target
112 }),
113 |(span, (name, _, _, target))| match target {
114 CursorTarget::Resultset(resultset) => DeclareCursor {
115 span: transform_span(span.tokens),
116 name,
117 stmt: None,
118 resultset: Some(resultset),
119 },
120 CursorTarget::Statement(stmt) => DeclareCursor {
121 span: transform_span(span.tokens),
122 name,
123 stmt: Some(stmt),
124 resultset: None,
125 },
126 },
127 )
128 .parse(i)
129}
130
131#[allow(clippy::large_enum_variant)]
132#[derive(Debug, Clone, PartialEq)]
133pub(crate) enum CursorTarget {
134 Resultset(Identifier),
135 Statement(Statement),
136}
137
138pub(crate) fn cursor_target(i: Input) -> IResult<CursorTarget> {
139 let resultset = map(ident, CursorTarget::Resultset);
141 let statement = map(statement_body, CursorTarget::Statement);
142
143 rule!(
144 #resultset
145 | #statement
146 )
147 .parse(i)
148}
149
150pub(crate) fn iterable_item(i: Input) -> IResult<IterableItem> {
151 map(ident, IterableItem::Resultset).parse(i)
155}
156
157pub fn script_stmts(i: Input) -> IResult<Vec<ScriptStatement>> {
158 semicolon_terminated_list1(script_stmt).parse(i)
159}
160
161#[recursive::recursive]
162pub fn script_stmt(i: Input) -> IResult<ScriptStatement> {
163 if let Some(token) = i.tokens.first() {
164 let kind = token.kind;
165 if matches!(kind, END | ELSE | ELSEIF | WHEN | UNTIL) {
166 return Err(nom::Err::Error(Error::from_error_kind(
167 i,
168 ErrorKind::Other("block terminator"),
169 )));
170 }
171 }
172 let let_var_stmt = map(
173 rule! {
174 LET ~ #declare_var
175 },
176 |(_, declare)| ScriptStatement::LetVar { declare },
177 );
178 let let_stmt_stmt = map(
179 rule! {
180 LET ~ #declare_set
181 },
182 |(_, declare)| ScriptStatement::LetStatement { declare },
183 );
184 let let_cursor_stmt = map(
185 rule! {
186 LET ~ #declare_cursor
187 },
188 |(_, declare)| ScriptStatement::LetCursor { declare },
189 );
190 let open_cursor_stmt = map(
191 consumed(rule! {
192 OPEN ~ #ident
193 }),
194 |(span, (_, cursor))| ScriptStatement::OpenCursor {
195 span: transform_span(span.tokens),
196 cursor,
197 },
198 );
199 let fetch_cursor_stmt = map(
200 consumed(rule! {
201 FETCH ~ #ident ~ ^INTO ~ ^#ident
202 }),
203 |(span, (_, cursor, _, into_var))| ScriptStatement::FetchCursor {
204 span: transform_span(span.tokens),
205 cursor,
206 into_var,
207 },
208 );
209 let close_cursor_stmt = map(
210 consumed(rule! {
211 CLOSE ~ #ident
212 }),
213 |(span, (_, cursor))| ScriptStatement::CloseCursor {
214 span: transform_span(span.tokens),
215 cursor,
216 },
217 );
218 let run_stmt = map(
219 consumed(rule! {
220 #statement_body
221 }),
222 |(span, stmt)| ScriptStatement::RunStatement {
223 span: transform_span(span.tokens),
224 stmt,
225 },
226 );
227 let assign_stmt = map(
228 consumed(rule! {
229 #ident ~ ":=" ~ ^#expr
230 }),
231 |(span, (name, _, value))| ScriptStatement::Assign {
232 span: transform_span(span.tokens),
233 name,
234 value,
235 },
236 );
237 let return_set_stmt = map(
238 consumed(rule! {
239 RETURN ~ TABLE ~ "(" ~ #ident ~ ^")"
240 }),
241 |(span, (_, _, _, name, _))| ScriptStatement::Return {
242 span: transform_span(span.tokens),
243 value: Some(ReturnItem::Set(name)),
244 },
245 );
246 let return_stmt_stmt = map(
247 consumed(rule! {
248 RETURN ~ TABLE ~ "(" ~ #statement_body ~ ^")"
249 }),
250 |(span, (_, _, _, stmt, _))| ScriptStatement::Return {
251 span: transform_span(span.tokens),
252 value: Some(ReturnItem::Statement(stmt)),
253 },
254 );
255 let return_var_stmt = map(
256 consumed(rule! {
257 RETURN ~ #expr
258 }),
259 |(span, (_, expr))| ScriptStatement::Return {
260 span: transform_span(span.tokens),
261 value: Some(ReturnItem::Var(expr)),
262 },
263 );
264 let return_stmt = map(
265 consumed(rule! {
266 RETURN
267 }),
268 |(span, _)| ScriptStatement::Return {
269 span: transform_span(span.tokens),
270 value: None,
271 },
272 );
273 let throw_stmt = map(
274 consumed(rule! {
275 THROW ~ #expr?
276 }),
277 |(span, (_, message))| ScriptStatement::Throw {
278 span: transform_span(span.tokens),
279 message,
280 },
281 );
282 let for_loop_stmt = map(
283 consumed(rule! {
284 FOR ~ ^#ident ~ ^IN ~ REVERSE?
285 ~ #expr ~ TO ~ #expr ~ ^DO
286 ~ ^#semicolon_terminated_list1(script_stmt)
287 ~ ^END ~ ^FOR ~ #ident?
288 }),
289 |(
290 span,
291 (_, variable, _, is_reverse, lower_bound, _, upper_bound, _, body, _, _, label),
292 )| ScriptStatement::ForLoop {
293 span: transform_span(span.tokens),
294 variable,
295 is_reverse: is_reverse.is_some(),
296 lower_bound,
297 upper_bound,
298 body,
299 label,
300 },
301 );
302 let for_in_set_stmt = map(
303 consumed(rule! {
304 FOR ~ ^#ident ~ ^IN ~ #iterable_item ~ ^DO
305 ~ ^#semicolon_terminated_list1(script_stmt)
306 ~ ^END ~ ^FOR ~ #ident?
307 }),
308 |(span, (_, variable, _, iterable, _, body, _, _, label))| ScriptStatement::ForInSet {
309 span: transform_span(span.tokens),
310 variable,
311 iterable,
312 body,
313 label,
314 },
315 );
316 let for_in_stmt_stmt = map(
317 consumed(rule! {
318 FOR ~ ^#ident ~ ^IN ~ ^#statement_body ~ ^DO
319 ~ ^#semicolon_terminated_list1(script_stmt)
320 ~ ^END ~ ^FOR ~ #ident?
321 }),
322 |(span, (_, variable, _, stmt, _, body, _, _, label))| ScriptStatement::ForInStatement {
323 span: transform_span(span.tokens),
324 variable,
325 stmt,
326 body,
327 label,
328 },
329 );
330 let while_loop_stmt = map(
331 consumed(rule! {
332 WHILE ~ ^#expr ~ ^DO
333 ~ ^#semicolon_terminated_list1(script_stmt)
334 ~ ^END ~ ^WHILE ~ #ident?
335 }),
336 |(span, (_, condition, _, body, _, _, label))| ScriptStatement::WhileLoop {
337 span: transform_span(span.tokens),
338 condition,
339 body,
340 label,
341 },
342 );
343 let repeat_loop_stmt = map(
344 consumed(rule! {
345 REPEAT
346 ~ ^#semicolon_terminated_list1(script_stmt)
347 ~ ^UNTIL ~ ^#expr
348 ~ ^END ~ ^REPEAT ~ #ident?
349 }),
350 |(span, (_, body, _, until_condition, _, _, label))| ScriptStatement::RepeatLoop {
351 span: transform_span(span.tokens),
352 body,
353 until_condition,
354 label,
355 },
356 );
357 let loop_stmt = map(
358 consumed(rule! {
359 LOOP ~ ^#semicolon_terminated_list1(script_stmt) ~ ^END ~ ^LOOP ~ #ident?
360 }),
361 |(span, (_, body, _, _, label))| ScriptStatement::Loop {
362 span: transform_span(span.tokens),
363 body,
364 label,
365 },
366 );
367 let break_stmt = map(
368 consumed(rule! {
369 BREAK ~ #ident?
370 }),
371 |(span, (_, label))| ScriptStatement::Break {
372 span: transform_span(span.tokens),
373 label,
374 },
375 );
376 let continue_stmt = map(
377 consumed(rule! {
378 CONTINUE ~ #ident?
379 }),
380 |(span, (_, label))| ScriptStatement::Continue {
381 span: transform_span(span.tokens),
382 label,
383 },
384 );
385 let case_stmt = map(
386 consumed(rule! {
387 CASE ~ #expr?
388 ~ ( WHEN ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt) )+
389 ~ ( ELSE ~ ^#semicolon_terminated_list1(script_stmt) )?
390 ~ ^END ~ CASE?
391 }),
392 |(span, (_, operand, branches, else_result, _, _))| {
393 let (conditions, results) = branches
394 .into_iter()
395 .map(|(_, cond, _, result)| (cond, result))
396 .unzip();
397 let else_result = else_result.map(|(_, result)| result);
398 ScriptStatement::Case {
399 span: transform_span(span.tokens),
400 operand,
401 conditions,
402 results,
403 else_result,
404 }
405 },
406 );
407 let if_stmt = map(
408 consumed(rule! {
409 IF ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt)
410 ~ ( ELSEIF ~ ^#expr ~ ^THEN ~ ^#semicolon_terminated_list1(script_stmt) )*
411 ~ ( ELSE ~ ^#semicolon_terminated_list1(script_stmt) )?
412 ~ ^END ~ ^IF
413 }),
414 |(span, (_, condition, _, result, else_ifs, else_result, _, _))| {
415 let (mut conditions, mut results) = (vec![condition], vec![result]);
416 for (_, cond, _, result) in else_ifs {
417 conditions.push(cond);
418 results.push(result);
419 }
420 let else_result = else_result.map(|(_, result)| result);
421 ScriptStatement::If {
422 span: transform_span(span.tokens),
423 conditions,
424 results,
425 else_result,
426 }
427 },
428 );
429
430 let cursor_stmts = rule!(
431 #open_cursor_stmt
432 | #fetch_cursor_stmt
433 | #close_cursor_stmt
434 );
435
436 let let_stmts = rule!(
437 #let_cursor_stmt
438 | #let_stmt_stmt
439 | #let_var_stmt
440 );
441
442 let assignment_stmts = rule!(
443 #let_stmts
444 | #assign_stmt
445 );
446
447 let control_flow_stmts = rule!(
448 #return_set_stmt
449 | #return_stmt_stmt
450 | #return_var_stmt
451 | #return_stmt
452 | #throw_stmt
453 | #break_stmt
454 | #continue_stmt
455 );
456
457 let loop_stmts = rule!(
458 #for_loop_stmt
459 | #for_in_set_stmt
460 | #for_in_stmt_stmt
461 | #while_loop_stmt
462 | #repeat_loop_stmt
463 | #loop_stmt
464 );
465
466 let conditional_stmts = rule!(
467 #case_stmt
468 | #if_stmt
469 );
470
471 rule!(
472 #assignment_stmts
473 | #cursor_stmts
474 | #control_flow_stmts
475 | #loop_stmts
476 | #conditional_stmts
477 | #run_stmt
478 )
479 .parse(i)
480}