surrealpatch/core/src/syn/parser/kind.rs
Micha de Vries 29af8f573d
Improve literal types ()
Co-authored-by: Raphael Darley <raphael.darley@surrealdb.com>
Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com>
2024-09-10 13:50:31 +01:00

541 lines
14 KiB
Rust

use std::collections::BTreeMap;
use reblessive::Stk;
use crate::{
sql::{kind::Literal, Kind, Strand},
syn::{
parser::mac::expected,
token::{t, Keyword, Span, TokenKind},
},
};
use super::{mac::unexpected, ParseResult, Parser};
impl Parser<'_> {
/// Parse a kind production.
///
/// # Parser State
/// expects the first `<` to already be eaten
pub async fn parse_kind(&mut self, ctx: &mut Stk, delim: Span) -> ParseResult<Kind> {
let kind = self.parse_inner_kind(ctx).await?;
self.expect_closing_delimiter(t!(">"), delim)?;
Ok(kind)
}
/// Parse an inner kind, a kind without enclosing `<` `>`.
pub async fn parse_inner_kind(&mut self, ctx: &mut Stk) -> ParseResult<Kind> {
match self.parse_inner_single_kind(ctx).await? {
Kind::Any => Ok(Kind::Any),
Kind::Option(k) => Ok(Kind::Option(k)),
first => {
if self.peek_kind() == t!("|") {
let mut kind = vec![first];
while self.eat(t!("|")) {
kind.push(ctx.run(|ctx| self.parse_concrete_kind(ctx)).await?);
}
let kind = Kind::Either(kind);
let kind = kind.to_discriminated().unwrap_or(kind);
Ok(kind)
} else {
Ok(first)
}
}
}
}
/// Parse a single inner kind, a kind without enclosing `<` `>`.
pub async fn parse_inner_single_kind(&mut self, ctx: &mut Stk) -> ParseResult<Kind> {
match self.peek_kind() {
t!("ANY") => {
self.pop_peek();
Ok(Kind::Any)
}
t!("OPTION") => {
self.pop_peek();
let delim = expected!(self, t!("<")).span;
let mut first = ctx.run(|ctx| self.parse_concrete_kind(ctx)).await?;
if self.peek_kind() == t!("|") {
let mut kind = vec![first];
while self.eat(t!("|")) {
kind.push(ctx.run(|ctx| self.parse_concrete_kind(ctx)).await?);
}
let kind = Kind::Either(kind);
first = kind.to_discriminated().unwrap_or(kind);
}
self.expect_closing_delimiter(t!(">"), delim)?;
Ok(Kind::Option(Box::new(first)))
}
_ => ctx.run(|ctx| self.parse_concrete_kind(ctx)).await,
}
}
/// Parse a single kind which is not any, option, or either.
async fn parse_concrete_kind(&mut self, ctx: &mut Stk) -> ParseResult<Kind> {
if Self::token_can_be_literal_kind(self.peek_kind()) {
let literal = self.parse_literal_kind(ctx).await?;
return Ok(Kind::Literal(literal));
}
let next = self.next();
match next.kind {
t!("BOOL") => Ok(Kind::Bool),
t!("NULL") => Ok(Kind::Null),
t!("BYTES") => Ok(Kind::Bytes),
t!("DATETIME") => Ok(Kind::Datetime),
t!("DECIMAL") => Ok(Kind::Decimal),
t!("DURATION") => Ok(Kind::Duration),
t!("FLOAT") => Ok(Kind::Float),
t!("INT") => Ok(Kind::Int),
t!("NUMBER") => Ok(Kind::Number),
t!("OBJECT") => Ok(Kind::Object),
t!("POINT") => Ok(Kind::Point),
t!("STRING") => Ok(Kind::String),
t!("UUID") => Ok(Kind::Uuid),
t!("RANGE") => Ok(Kind::Range),
t!("FUNCTION") => Ok(Kind::Function(Default::default(), Default::default())),
t!("RECORD") => {
let span = self.peek().span;
if self.eat(t!("<")) {
let mut tables = vec![self.next_token_value()?];
while self.eat(t!("|")) {
tables.push(self.next_token_value()?);
}
self.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Record(tables))
} else {
Ok(Kind::Record(Vec::new()))
}
}
t!("GEOMETRY") => {
let span = self.peek().span;
if self.eat(t!("<")) {
let mut kind = vec![self.parse_geometry_kind()?];
while self.eat(t!("|")) {
kind.push(self.parse_geometry_kind()?);
}
self.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Geometry(kind))
} else {
Ok(Kind::Geometry(Vec::new()))
}
}
t!("ARRAY") => {
let span = self.peek().span;
if self.eat(t!("<")) {
let kind = ctx.run(|ctx| self.parse_inner_kind(ctx)).await?;
let size = self.eat(t!(",")).then(|| self.next_token_value()).transpose()?;
self.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Array(Box::new(kind), size))
} else {
Ok(Kind::Array(Box::new(Kind::Any), None))
}
}
t!("SET") => {
let span = self.peek().span;
if self.eat(t!("<")) {
let kind = ctx.run(|ctx| self.parse_inner_kind(ctx)).await?;
let size = self.eat(t!(",")).then(|| self.next_token_value()).transpose()?;
self.expect_closing_delimiter(t!(">"), span)?;
Ok(Kind::Set(Box::new(kind), size))
} else {
Ok(Kind::Set(Box::new(Kind::Any), None))
}
}
_ => unexpected!(self, next, "a kind name"),
}
}
/// Parse the kind of gemoetry
fn parse_geometry_kind(&mut self) -> ParseResult<String> {
let next = self.next();
match next.kind {
TokenKind::Keyword(
x @ (Keyword::Feature
| Keyword::Point
| Keyword::Line
| Keyword::Polygon
| Keyword::MultiPoint
| Keyword::MultiLine
| Keyword::MultiPolygon
| Keyword::Collection),
) => Ok(x.as_str().to_ascii_lowercase()),
_ => unexpected!(self, next, "a geometry kind name"),
}
}
/// Parse a literal kind
async fn parse_literal_kind(&mut self, ctx: &mut Stk) -> ParseResult<Literal> {
let peek = self.peek();
match peek.kind {
t!("'") | t!("\"") | TokenKind::Strand => {
let s = self.next_token_value::<Strand>()?;
Ok(Literal::String(s))
}
t!("+") | t!("-") | TokenKind::Number(_) | TokenKind::Digits | TokenKind::Duration => {
let token = self.glue_numeric()?;
match token.kind {
TokenKind::Number(_) => self.next_token_value().map(Literal::Number),
TokenKind::Duration => self.next_token_value().map(Literal::Duration),
_ => unexpected!(self, token, "a value"),
}
}
t!("{") => {
self.pop_peek();
let mut obj = BTreeMap::new();
while !self.eat(t!("}")) {
let key = self.parse_object_key()?;
expected!(self, t!(":"));
let kind = ctx.run(|ctx| self.parse_inner_kind(ctx)).await?;
obj.insert(key, kind);
self.eat(t!(","));
}
Ok(Literal::Object(obj))
}
t!("[") => {
self.pop_peek();
let mut arr = Vec::new();
while !self.eat(t!("]")) {
let kind = ctx.run(|ctx| self.parse_inner_kind(ctx)).await?;
arr.push(kind);
self.eat(t!(","));
}
Ok(Literal::Array(arr))
}
_ => unexpected!(self, peek, "a literal kind"),
}
}
fn token_can_be_literal_kind(t: TokenKind) -> bool {
matches!(
t,
t!("'")
| t!("\"") | TokenKind::Strand
| t!("+") | t!("-")
| TokenKind::Number(_)
| TokenKind::Digits
| TokenKind::Duration
| t!("{") | t!("[")
)
}
}
#[cfg(test)]
mod tests {
use reblessive::Stack;
use super::*;
use crate::sql::table::Table;
fn kind(i: &str) -> ParseResult<Kind> {
let mut parser = Parser::new(i.as_bytes());
let mut stack = Stack::new();
stack.enter(|ctx| parser.parse_inner_kind(ctx)).finish()
}
#[test]
fn kind_any() {
let sql = "any";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("any", format!("{}", out));
assert_eq!(out, Kind::Any);
}
#[test]
fn kind_null() {
let sql = "null";
let res = kind(sql);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("null", format!("{}", out));
assert_eq!(out, Kind::Null);
}
#[test]
fn kind_bool() {
let sql = "bool";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("bool", format!("{}", out));
assert_eq!(out, Kind::Bool);
}
#[test]
fn kind_bytes() {
let sql = "bytes";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("bytes", format!("{}", out));
assert_eq!(out, Kind::Bytes);
}
#[test]
fn kind_datetime() {
let sql = "datetime";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("datetime", format!("{}", out));
assert_eq!(out, Kind::Datetime);
}
#[test]
fn kind_decimal() {
let sql = "decimal";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("decimal", format!("{}", out));
assert_eq!(out, Kind::Decimal);
}
#[test]
fn kind_duration() {
let sql = "duration";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("duration", format!("{}", out));
assert_eq!(out, Kind::Duration);
}
#[test]
fn kind_float() {
let sql = "float";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("float", format!("{}", out));
assert_eq!(out, Kind::Float);
}
#[test]
fn kind_number() {
let sql = "number";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("number", format!("{}", out));
assert_eq!(out, Kind::Number);
}
#[test]
fn kind_object() {
let sql = "object";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("object", format!("{}", out));
assert_eq!(out, Kind::Object);
}
#[test]
fn kind_point() {
let sql = "point";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("point", format!("{}", out));
assert_eq!(out, Kind::Point);
}
#[test]
fn kind_string() {
let sql = "string";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("string", format!("{}", out));
assert_eq!(out, Kind::String);
}
#[test]
fn kind_uuid() {
let sql = "uuid";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("uuid", format!("{}", out));
assert_eq!(out, Kind::Uuid);
}
#[test]
fn kind_either() {
let sql = "int | float";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("int | float", format!("{}", out));
assert_eq!(out, Kind::Either(vec![Kind::Int, Kind::Float]));
}
#[test]
fn kind_record_any() {
let sql = "record";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("record", format!("{}", out));
assert_eq!(out, Kind::Record(vec![]));
}
#[test]
fn kind_record_one() {
let sql = "record<person>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("record<person>", format!("{}", out));
assert_eq!(out, Kind::Record(vec![Table::from("person")]));
}
#[test]
fn kind_record_many() {
let sql = "record<person | animal>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("record<person | animal>", format!("{}", out));
assert_eq!(out, Kind::Record(vec![Table::from("person"), Table::from("animal")]));
}
#[test]
fn kind_geometry_any() {
let sql = "geometry";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("geometry", format!("{}", out));
assert_eq!(out, Kind::Geometry(vec![]));
}
#[test]
fn kind_geometry_one() {
let sql = "geometry<point>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("geometry<point>", format!("{}", out));
assert_eq!(out, Kind::Geometry(vec![String::from("point")]));
}
#[test]
fn kind_geometry_many() {
let sql = "geometry<point | multipoint>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("geometry<point | multipoint>", format!("{}", out));
assert_eq!(out, Kind::Geometry(vec![String::from("point"), String::from("multipoint")]));
}
#[test]
fn kind_option_one() {
let sql = "option<int>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("option<int>", format!("{}", out));
assert_eq!(out, Kind::Option(Box::new(Kind::Int)));
}
#[test]
fn kind_option_many() {
let sql = "option<int | float>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("option<int | float>", format!("{}", out));
assert_eq!(out, Kind::Option(Box::new(Kind::Either(vec![Kind::Int, Kind::Float]))));
}
#[test]
fn kind_array_any() {
let sql = "array";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("array", format!("{}", out));
assert_eq!(out, Kind::Array(Box::new(Kind::Any), None));
}
#[test]
fn kind_array_some() {
let sql = "array<float>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("array<float>", format!("{}", out));
assert_eq!(out, Kind::Array(Box::new(Kind::Float), None));
}
#[test]
fn kind_array_some_size() {
let sql = "array<float, 10>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("array<float, 10>", format!("{}", out));
assert_eq!(out, Kind::Array(Box::new(Kind::Float), Some(10)));
}
#[test]
fn kind_set_any() {
let sql = "set";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("set", format!("{}", out));
assert_eq!(out, Kind::Set(Box::new(Kind::Any), None));
}
#[test]
fn kind_set_some() {
let sql = "set<float>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("set<float>", format!("{}", out));
assert_eq!(out, Kind::Set(Box::new(Kind::Float), None));
}
#[test]
fn kind_set_some_size() {
let sql = "set<float, 10>";
let res = kind(sql);
let out = res.unwrap();
assert_eq!("set<float, 10>", format!("{}", out));
assert_eq!(out, Kind::Set(Box::new(Kind::Float), Some(10)));
}
#[test]
fn kind_discriminated_object() {
let sql = "{ status: 'ok', data: object } | { status: 'error', message: string }";
let res = kind(sql);
let out = res.unwrap();
assert_eq!(
"{ data: object, status: 'ok' } | { message: string, status: 'error' }",
format!("{}", out)
);
assert_eq!(
out,
Kind::Literal(Literal::DiscriminatedObject(
"status".to_string(),
vec![
map! {
"status".to_string() => Kind::Literal(Literal::String("ok".into())),
"data".to_string() => Kind::Object,
},
map! {
"status".to_string() => Kind::Literal(Literal::String("error".into())),
"message".to_string() => Kind::String,
},
]
))
);
}
#[test]
fn kind_union_literal_object() {
let sql = "{ status: 'ok', data: object } | { status: string, message: string }";
let res = kind(sql);
let out = res.unwrap();
assert_eq!(
"{ data: object, status: 'ok' } | { message: string, status: string }",
format!("{}", out)
);
assert_eq!(
out,
Kind::Either(vec![
Kind::Literal(Literal::Object(map! {
"status".to_string() => Kind::Literal(Literal::String("ok".into())),
"data".to_string() => Kind::Object,
})),
Kind::Literal(Literal::Object(map! {
"status".to_string() => Kind::String,
"message".to_string() => Kind::String,
})),
])
);
}
}