rupg-sql 0.1.84

The lexer, the parser from the vendored gram.y, the parse tree and its printer.
Documentation
//! The actions of `gram.y` that build the raw parse tree, with the functions that the actions call.
//!
//! `cargo xtask sql` translates most actions from C to Rust and writes them into `src/generated/glue.rs`. An action that the translator cannot write, for example one with a loop or a `switch`, is a method of a trait, one trait for each rule of `gram.y`. The default of a method gives the error that the action is not ported. A file here implements the trait of a rule for [`Parser`] and ports the C of each such alternative by hand, line by line. After a new `impl`, `cargo xtask sql` removes the empty `impl` of that trait from the glue, and when the translator can write an action that has a port here, it fails until the port is removed.
//!
//! The arguments of a method are the values `$k` that the C uses, then the locations `@k`, then `@$`, and a port names them `vk`, `atk` and `here`. Bison sets `$$` to `$1` before an action, so a port of an action that does not always set `$$` gives `$1` where the C sets nothing.
//!
//! [`make`] has the functions of `makefuncs.c` and `value.c` that the actions use, [`funcs`] those of `nodeFuncs.c`, [`list`] the list and cast macros of `pg_list.h` and `nodes.h`, and [`gram`] the static functions of the prologue and the epilogue of `gram.y`. They keep the C names, so that a port reads as the C does. The translated actions call them too: a `pub(crate) fn` at the start of a line in these files, with no generic parameters, is a function that the translator knows by its signature. A result of `Result<T, Error>` tells it that the function can fail.
//!
//! Lifted from `crates/rudb-pgparse/src/actions/mod.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).

#![allow(non_snake_case)]

mod expr;
mod funcs;
mod gram;
mod json;
mod list;
mod make;
mod names;
mod select;
mod stmt;
mod types;

pub(crate) use gram::*;
pub(crate) use list::*;
pub(crate) use make::*;

use std::vec::Drain;

use rupg_common::SqlState;

use crate::error::{Error, Notice, Severity};
use crate::filter::Tokens;
use crate::generated::glue::{self, Value};
use crate::lexer::Lexer;
use crate::nodes::List;
use crate::parser::Machine;

/// The state of a parse with actions, as `base_yy_extra_type` and the scanner have it in C.
pub(crate) struct Parser<'a> {
    /// The text that the parser reads, up to the first zero byte.
    text: &'a str,
    tokens: Tokens<'a>,
    /// The start and the end of the last token that the parser read, for `parser_yyerror`.
    last: (usize, usize),
    /// The notices of the lexer and of the actions, in order.
    notices: Vec<Notice>,
    /// The `parsetree` of `base_yy_extra_type`: the list that the start rule gives.
    pub(crate) parsetree: List,
    /// The token of the mode of `raw_parser`, such as `MODE_TYPE_NAME`, which the parser reads before the text. `None` parses statements.
    mode: Option<u16>,
}

impl<'a> Parser<'a> {
    pub(crate) fn new(text: &'a str) -> Parser<'a> {
        Parser::with_mode(text, None)
    }

    /// A parser that reads the token `mode` first, as `raw_parser` does for a mode other than `RAW_PARSE_DEFAULT`.
    pub(crate) fn with_mode(text: &'a str, mode: Option<u16>) -> Parser<'a> {
        let text = Lexer::new(text).text();
        Parser {
            text,
            tokens: Tokens::new(text),
            last: (0, 0),
            notices: Vec::new(),
            parsetree: List::new(),
            mode,
        }
    }

    /// `parser_yyerror`: an error at the last token that the parser read.
    pub(crate) fn yyerror(&self, message: &str) -> Error {
        Error::syntax(self.text.as_bytes(), message, self.last.0, self.last.1)
    }

    /// `ereport(WARNING, errmsg(message), parser_errposition(location))`.
    pub(crate) fn warning(&mut self, message: &str, location: i32) {
        self.notices.push(Notice {
            severity: Severity::Warning,
            code: SqlState::WARNING,
            message: message.to_owned(),
            location: usize::try_from(location).ok(),
        });
    }

    /// The tree and all the notices.
    pub(crate) fn finish(mut self) -> (List, Vec<Notice>) {
        self.notices.append(&mut self.tokens.take_notices());
        (self.parsetree, self.notices)
    }
}

impl Machine for Parser<'_> {
    type Value = Value;
    type Error = Error;

    fn token(&mut self) -> Result<(u16, Value, i32), Error> {
        if let Some(mode) = self.mode.take() {
            return Ok((mode, Value::default(), 0));
        }
        let token = self.tokens.next_token()?;
        // The notices of the lexer come before those of the actions that run after this token.
        self.notices.append(&mut self.tokens.take_notices());
        self.last = (token.start, token.end);
        let location = i32::try_from(token.start).unwrap_or(i32::MAX);
        Ok((token.kind, glue::token(token.value), location))
    }

    fn reduce(
        &mut self,
        rule: usize,
        rhs: Drain<'_, Value>,
        at: &[i32],
        here: i32,
    ) -> Result<Value, Error> {
        glue::reduce(self, rule, rhs, at, here)
    }
}

/// The values of the right side of a rule, in order. The parser gives as many values as the rule has symbols, so the array is always full.
pub(crate) fn take<const N: usize>(mut rhs: Drain<'_, Value>) -> [Value; N] {
    std::array::from_fn(|_| rhs.next().unwrap_or_default())
}