ankql 0.7.23

Ankurah Query Language - Aspirational query language for Ankurah in the style of Open Cypher and GQL (ISO/IEC 39075:2024)
Documentation
// Stripped down version of the SQL grammar focusing on Selection
// From https://raw.githubusercontent.com/pest-parser/pest/refs/heads/master/grammars/src/grammars/sql.pest

Selection = _{ SOI ~ Expr ~ OrderByClause? ~ LimitClause? ~ EOI }

Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* }
    ExprInfixOp = _{ Between | ArithInfixOp | CmpInfixOp | And | Or }
        Between       = { NotFlag? ~ ^"between" }
        And           = @{ ^"and" ~ !IDENT_CONT }
        Or            = @{ ^"or" ~ !IDENT_CONT }
        ArithInfixOp  = _{ Add | Subtract | Multiply | Divide }
            Add      = { "+" }
            Subtract = { "-" }
            Multiply = { "*" }
            Divide   = { "/" }
        CmpInfixOp    = _{ NotEq | GtEq | Gt | LtEq | Lt | Eq | Lt | In }
            Eq    = { "=" }
            Gt    = { ">" }
            GtEq  = { ">=" }
            Lt    = { "<" }
            LtEq  = { "<=" }
            NotEq = { "<>" | "!=" }
            In    = { NotFlag? ~ ^"in" }
    ExprAtomValue = _{ UnaryNot* ~ AtomicExpr ~ IsNullPostfix? }
        UnaryNot   = @{ NotFlag }
        IsNullPostfix = { ^"is" ~ NotFlag? ~ ^"null" }
        AtomicExpr = _{ Literal | QuestionParameter | PathExpr | ExpressionInParentheses | Row }
            Literal = _{ True | False | Null | Double | Decimal | Unsigned | Integer | SingleQuotedString }
                True     = @{ ^"true" ~ !IDENT_CONT }
                False    = @{ ^"false" ~ !IDENT_CONT }
                Null     = @{ ^"null" ~ !IDENT_CONT }
                Decimal = @{ Integer ~ ("." ~ ASCII_DIGIT*) }
                Double = @{ Integer ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ Integer) }
                Integer = @{ ("+" | "-")? ~ ASCII_DIGIT+ }
                Unsigned = @{ ASCII_DIGIT+ }
                SingleQuotedString = @{ OnlyQuotesSequence | AnythingButQuotesSequence }
                    OnlyQuotesSequence = @{ ("'" ~ "'")+ }
                    AnythingButQuotesSequence = @{ "'" ~ (!("'") ~ ANY)* ~ "'" }
            QuestionParameter = @{ "?" }
            PathExpr = { Identifier ~ ("." ~ Identifier)* }
            ExpressionInParentheses = { "(" ~ Expr ~ ")" }
            Row = { "(" ~ Expr ~ ("," ~ Expr)* ~ ")" }

Identifier = @{ DoubleQuotedIdentifier | IdentifierInner  }
    DoubleQuotedIdentifier = @{ ("\"" ~ IdentifierInner ~ "\"") }
    IdentifierInner = @{ !(Reserved ~ ("(" | WHITESPACE | "," | EOF)) ~ (IdentifierNonDigit ~ (IdentifierNonDigit | ASCII_DIGIT)*) }
        IdentifierNonDigit = _{ ('a'..'z' | 'A' .. 'Z' | 'А' .. 'Я' | 'а' .. 'я' | "-" | "_") }
    Reserved = { ^"left" | ^"having" | ^"not" | ^"inner" | ^"group"
                | ^"on" | ^"join" | ^"from" | ^"exists" | ^"except"
                | ^"union" | ^"where" | ^"distinct" | ^"between" | ^"option"
                | ^"values" | LimitClause | OrderByClause }

OrderByClause = ${ ^"order" ~ WS+ ~ ^"by" ~ WS+ ~ (OrderByItem ~ WS* ~ ("," ~ WS* ~ OrderByItem)*)? }
OrderByItem = ${ Identifier ~ (WS+ ~ OrderDirection)? }
OrderDirection = { ^"asc" | ^"desc" }
LimitClause = ${ ^"limit" ~ WS+ ~ Unsigned }

NotFlag = { ^"not" }

EOF = { EOI | ";" }
WS = _{ WHITESPACE }
WHITESPACE = _{ " " | "\t" | "\n" | "\r\n" }
IDENT_CONT  = _{ ASCII_ALPHANUMERIC | "_" }