Skip to main content

databend_common_ast/parser/
script.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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    // Try identifier first, then statement
140    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    // For now, we'll treat all identifiers as potential iterables
152    // The compiler will determine if it's a cursor or resultset
153    // based on what was actually declared
154    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}