/* * Copyright (c) 2021, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include "Parser.h" #include namespace SQL { Parser::Parser(Lexer lexer) : m_parser_state(move(lexer)) { } NonnullRefPtr Parser::next_statement() { auto terminate_statement = [this](auto statement) { consume(TokenType::SemiColon); return statement; }; if (match(TokenType::With)) { auto common_table_expression_list = parse_common_table_expression_list(); return terminate_statement(parse_statement_with_expression_list(move(common_table_expression_list))); } return terminate_statement(parse_statement()); } NonnullRefPtr Parser::parse_statement() { switch (m_parser_state.m_token.type()) { case TokenType::Create: return parse_create_table_statement(); case TokenType::Alter: return parse_alter_table_statement(); case TokenType::Drop: return parse_drop_table_statement(); case TokenType::Insert: return parse_insert_statement({}); case TokenType::Update: return parse_update_statement({}); case TokenType::Delete: return parse_delete_statement({}); case TokenType::Select: return parse_select_statement({}); default: expected("CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, or SELECT"); return create_ast_node(); } } NonnullRefPtr Parser::parse_statement_with_expression_list(RefPtr common_table_expression_list) { switch (m_parser_state.m_token.type()) { case TokenType::Insert: return parse_insert_statement(move(common_table_expression_list)); case TokenType::Update: return parse_update_statement(move(common_table_expression_list)); case TokenType::Delete: return parse_delete_statement(move(common_table_expression_list)); case TokenType::Select: return parse_select_statement(move(common_table_expression_list)); default: expected("INSERT, UPDATE, DELETE or SELECT"); return create_ast_node(); } } NonnullRefPtr Parser::parse_create_table_statement() { // https://sqlite.org/lang_createtable.html consume(TokenType::Create); bool is_temporary = false; if (consume_if(TokenType::Temp) || consume_if(TokenType::Temporary)) is_temporary = true; consume(TokenType::Table); bool is_error_if_table_exists = true; if (consume_if(TokenType::If)) { consume(TokenType::Not); consume(TokenType::Exists); is_error_if_table_exists = false; } String schema_name; String table_name; parse_schema_and_table_name(schema_name, table_name); if (consume_if(TokenType::As)) { auto select_statement = parse_select_statement({}); return create_ast_node(move(schema_name), move(table_name), move(select_statement), is_temporary, is_error_if_table_exists); } NonnullRefPtrVector column_definitions; parse_comma_separated_list(true, [&]() { column_definitions.append(parse_column_definition()); }); // FIXME: Parse "table-constraint". return create_ast_node(move(schema_name), move(table_name), move(column_definitions), is_temporary, is_error_if_table_exists); } NonnullRefPtr Parser::parse_alter_table_statement() { // https://sqlite.org/lang_altertable.html consume(TokenType::Alter); consume(TokenType::Table); String schema_name; String table_name; parse_schema_and_table_name(schema_name, table_name); if (consume_if(TokenType::Add)) { consume_if(TokenType::Column); // COLUMN is optional. auto column = parse_column_definition(); return create_ast_node(move(schema_name), move(table_name), move(column)); } if (consume_if(TokenType::Drop)) { consume_if(TokenType::Column); // COLUMN is optional. auto column = consume(TokenType::Identifier).value(); return create_ast_node(move(schema_name), move(table_name), move(column)); } consume(TokenType::Rename); if (consume_if(TokenType::To)) { auto new_table_name = consume(TokenType::Identifier).value(); return create_ast_node(move(schema_name), move(table_name), move(new_table_name)); } consume_if(TokenType::Column); // COLUMN is optional. auto column_name = consume(TokenType::Identifier).value(); consume(TokenType::To); auto new_column_name = consume(TokenType::Identifier).value(); return create_ast_node(move(schema_name), move(table_name), move(column_name), move(new_column_name)); } NonnullRefPtr Parser::parse_drop_table_statement() { // https://sqlite.org/lang_droptable.html consume(TokenType::Drop); consume(TokenType::Table); bool is_error_if_table_does_not_exist = true; if (consume_if(TokenType::If)) { consume(TokenType::Exists); is_error_if_table_does_not_exist = false; } String schema_name; String table_name; parse_schema_and_table_name(schema_name, table_name); return create_ast_node(move(schema_name), move(table_name), is_error_if_table_does_not_exist); } NonnullRefPtr Parser::parse_insert_statement(RefPtr common_table_expression_list) { // https://sqlite.org/lang_insert.html consume(TokenType::Insert); auto conflict_resolution = parse_conflict_resolution(); consume(TokenType::Into); String schema_name; String table_name; parse_schema_and_table_name(schema_name, table_name); String alias; if (consume_if(TokenType::As)) alias = consume(TokenType::Identifier).value(); Vector column_names; if (match(TokenType::ParenOpen)) parse_comma_separated_list(true, [&]() { column_names.append(consume(TokenType::Identifier).value()); }); NonnullRefPtrVector chained_expressions; RefPtr Parser::parse_select_statement(RefPtr common_table_expression_list) { // https://sqlite.org/lang_select.html consume(TokenType::Select); bool select_all = !consume_if(TokenType::Distinct); consume_if(TokenType::All); // ALL is the default, so ignore it if specified. NonnullRefPtrVector result_column_list; parse_comma_separated_list(false, [&]() { result_column_list.append(parse_result_column()); }); NonnullRefPtrVector table_or_subquery_list; if (consume_if(TokenType::From)) { // FIXME: Parse join-clause. parse_comma_separated_list(false, [&]() { table_or_subquery_list.append(parse_table_or_subquery()); }); } RefPtr where_clause; if (consume_if(TokenType::Where)) where_clause = parse_expression(); RefPtr group_by_clause; if (consume_if(TokenType::Group)) { consume(TokenType::By); NonnullRefPtrVector group_by_list; parse_comma_separated_list(false, [&]() { group_by_list.append(parse_expression()); }); if (!group_by_list.is_empty()) { RefPtr having_clause; if (consume_if(TokenType::Having)) having_clause = parse_expression(); group_by_clause = create_ast_node(move(group_by_list), move(having_clause)); } } // FIXME: Parse 'WINDOW window-name AS window-defn'. // FIXME: Parse 'compound-operator'. NonnullRefPtrVector ordering_term_list; if (consume_if(TokenType::Order)) { consume(TokenType::By); parse_comma_separated_list(false, [&]() { ordering_term_list.append(parse_ordering_term()); }); } RefPtr limit_clause; if (consume_if(TokenType::Limit)) { auto limit_expression = parse_expression(); RefPtr offset_expression; if (consume_if(TokenType::Offset)) { offset_expression = parse_expression(); } else { // Note: The limit clause may instead be defined as "offset-expression, limit-expression", effectively reversing the // order of the expressions. SQLite notes "this is counter-intuitive" and "to avoid confusion, programmers are strongly // encouraged to ... avoid using a LIMIT clause with a comma-separated offset." VERIFY(!consume_if(TokenType::Comma)); } limit_clause = create_ast_node(move(limit_expression), move(offset_expression)); } return create_ast_node