Skip to content

Commit

Permalink
Merge pull request #5309 from andylokandy/span
Browse files Browse the repository at this point in the history
feat(parser): add span for expression
  • Loading branch information
BohuTANG authored May 11, 2022
2 parents 2fdff1a + b5b2e5a commit 5251ba2
Show file tree
Hide file tree
Showing 12 changed files with 1,203 additions and 205 deletions.
111 changes: 92 additions & 19 deletions common/ast/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,70 +19,86 @@ use crate::ast::write_comma_separated_list;
use crate::ast::write_period_separated_list;
use crate::ast::Identifier;
use crate::ast::Query;
use crate::parser::token::Token;

#[derive(Debug, Clone, PartialEq)]
pub enum Expr<'a> {
/// Column reference, with indirection like `table.column`
ColumnRef {
span: &'a [Token<'a>],
database: Option<Identifier<'a>>,
table: Option<Identifier<'a>>,
column: Identifier<'a>,
},
/// `IS [ NOT ] NULL` expression
IsNull { expr: Box<Expr<'a>>, not: bool },
IsNull {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
not: bool,
},
/// `[ NOT ] IN (expr, ...)`
InList {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
list: Vec<Expr<'a>>,
not: bool,
},
/// `[ NOT ] IN (SELECT ...)`
InSubquery {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
subquery: Box<Query<'a>>,
not: bool,
},
/// `BETWEEN ... AND ...`
Between {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
low: Box<Expr<'a>>,
high: Box<Expr<'a>>,
not: bool,
},
/// Binary operation
BinaryOp {
span: &'a [Token<'a>],
op: BinaryOperator,
left: Box<Expr<'a>>,
right: Box<Expr<'a>>,
},
/// Unary operation
UnaryOp {
span: &'a [Token<'a>],
op: UnaryOperator,
expr: Box<Expr<'a>>,
},
/// `CAST` expression, like `CAST(expr AS target_type)`
Cast {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
target_type: TypeName,
pg_style: bool,
},
/// `TRY_CAST` expression`
TryCast {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
target_type: TypeName,
},
/// EXTRACT(DateTimeField FROM <expr>)
Extract {
span: &'a [Token<'a>],
field: DateTimeField,
expr: Box<Expr<'a>>,
},
/// POSITION(<expr> IN <expr>)
Position {
span: &'a [Token<'a>],
substr_expr: Box<Expr<'a>>,
str_expr: Box<Expr<'a>>,
},
/// SUBSTRING(<expr> [FROM <expr>] [FOR <expr>])
Substring {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
substring_from: Option<Box<Expr<'a>>>,
substring_for: Option<Box<Expr<'a>>>,
Expand All @@ -91,18 +107,23 @@ pub enum Expr<'a> {
/// Or
/// TRIM(<expr>)
Trim {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
// ([BOTH | LEADING | TRAILING], <expr>)
trim_where: Option<(TrimWhere, Box<Expr<'a>>)>,
},
/// A literal value, such as string, number, date or NULL
Literal(Literal),
Literal { span: &'a [Token<'a>], lit: Literal },
/// `COUNT(*)` expression
CountAll,
CountAll { span: &'a [Token<'a>] },
/// `(foo, bar)`
Tuple { exprs: Vec<Expr<'a>> },
Tuple {
span: &'a [Token<'a>],
exprs: Vec<Expr<'a>>,
},
/// Scalar function call
FunctionCall {
span: &'a [Token<'a>],
/// Set to true if the function is aggregate function with `DISTINCT`, like `COUNT(DISTINCT a)`
distinct: bool,
name: Identifier<'a>,
Expand All @@ -111,18 +132,26 @@ pub enum Expr<'a> {
},
/// `CASE ... WHEN ... ELSE ...` expression
Case {
span: &'a [Token<'a>],
operand: Option<Box<Expr<'a>>>,
conditions: Vec<Expr<'a>>,
results: Vec<Expr<'a>>,
else_result: Option<Box<Expr<'a>>>,
},
/// `EXISTS` expression
Exists(Box<Query<'a>>),
Exists {
span: &'a [Token<'a>],
subquery: Box<Query<'a>>,
},
/// Scalar subquery, which will only return a single row with a single column.
Subquery(Box<Query<'a>>),
Subquery {
span: &'a [Token<'a>],
subquery: Box<Query<'a>>,
},
// TODO(andylokandy): allow interval, function, and others alike to be a key
/// Access elements of `Array`, `Object` and `Variant` by index or key, like `arr[0]`, or `obj:k1`
MapAccess {
span: &'a [Token<'a>],
expr: Box<Expr<'a>>,
accessor: MapAccessor<'a>,
},
Expand Down Expand Up @@ -252,6 +281,34 @@ pub enum UnaryOperator {
Not,
}

impl<'a> Expr<'a> {
pub fn span(&self) -> &'a [Token<'a>] {
match self {
Expr::ColumnRef { span, .. } => span,
Expr::IsNull { span, .. } => span,
Expr::InList { span, .. } => span,
Expr::InSubquery { span, .. } => span,
Expr::Between { span, .. } => span,
Expr::BinaryOp { span, .. } => span,
Expr::UnaryOp { span, .. } => span,
Expr::Cast { span, .. } => span,
Expr::TryCast { span, .. } => span,
Expr::Extract { span, .. } => span,
Expr::Position { span, .. } => span,
Expr::Substring { span, .. } => span,
Expr::Trim { span, .. } => span,
Expr::Literal { span, .. } => span,
Expr::CountAll { span } => span,
Expr::Tuple { span, .. } => span,
Expr::FunctionCall { span, .. } => span,
Expr::Case { span, .. } => span,
Expr::Exists { span, .. } => span,
Expr::Subquery { span, .. } => span,
Expr::MapAccess { span, .. } => span,
}
}
}

impl Display for UnaryOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -493,17 +550,20 @@ impl<'a> Display for Expr<'a> {
database,
table,
column,
..
} => {
write_period_separated_list(f, database.iter().chain(table).chain(Some(column)))?;
}
Expr::IsNull { expr, not } => {
Expr::IsNull { expr, not, .. } => {
write!(f, "{expr} IS")?;
if *not {
write!(f, " NOT")?;
}
write!(f, " NULL")?;
}
Expr::InList { expr, list, not } => {
Expr::InList {
expr, list, not, ..
} => {
write!(f, "{expr}")?;
if *not {
write!(f, " NOT")?;
Expand All @@ -516,6 +576,7 @@ impl<'a> Display for Expr<'a> {
expr,
subquery,
not,
..
} => {
write!(f, "{expr}")?;
if *not {
Expand All @@ -528,46 +589,54 @@ impl<'a> Display for Expr<'a> {
low,
high,
not,
..
} => {
write!(f, "{expr}")?;
if *not {
write!(f, " NOT")?;
}
write!(f, " BETWEEN {low} AND {high}")?;
}
Expr::UnaryOp { op, expr } => {
Expr::UnaryOp { op, expr, .. } => {
write!(f, "{op} {expr}")?;
}
Expr::BinaryOp { op, left, right } => {
Expr::BinaryOp {
op, left, right, ..
} => {
write!(f, "{left} {op} {right}")?;
}
Expr::Cast {
expr,
target_type,
pg_style,
..
} => {
if *pg_style {
write!(f, "{expr}::{target_type}")?;
} else {
write!(f, "CAST({expr} AS {target_type})")?;
}
}
Expr::TryCast { expr, target_type } => {
Expr::TryCast {
expr, target_type, ..
} => {
write!(f, "TRY_CAST({expr} AS {target_type})")?;
}
Expr::Extract { field, expr } => {
Expr::Extract { field, expr, .. } => {
write!(f, "EXTRACT({field} FROM {expr})")?;
}
Expr::Position {
substr_expr,
str_expr,
..
} => {
write!(f, "POSITION({substr_expr} IN {str_expr})")?;
}
Expr::Substring {
expr,
substring_from,
substring_for,
..
} => {
write!(f, "SUBSTRING({expr}")?;
if let Some(substring_from) = substring_from {
Expand All @@ -578,20 +647,22 @@ impl<'a> Display for Expr<'a> {
}
write!(f, ")")?;
}
Expr::Trim { expr, trim_where } => {
Expr::Trim {
expr, trim_where, ..
} => {
write!(f, "TRIM(")?;
if let Some((trim_where, trim_str)) = trim_where {
write!(f, "{trim_where} {trim_str} FROM ")?;
}
write!(f, "{expr})")?;
}
Expr::Literal(lit) => {
Expr::Literal { lit, .. } => {
write!(f, "{lit}")?;
}
Expr::CountAll => {
Expr::CountAll { .. } => {
write!(f, "COUNT(*)")?;
}
Expr::Tuple { exprs } => {
Expr::Tuple { exprs, .. } => {
write!(f, "(")?;
write_comma_separated_list(f, exprs)?;
if exprs.len() == 1 {
Expand All @@ -604,6 +675,7 @@ impl<'a> Display for Expr<'a> {
name,
args,
params,
..
} => {
write!(f, "{name}")?;
if !params.is_empty() {
Expand All @@ -623,6 +695,7 @@ impl<'a> Display for Expr<'a> {
conditions,
results,
else_result,
..
} => {
write!(f, "CASE")?;
if let Some(op) = operand {
Expand All @@ -636,13 +709,13 @@ impl<'a> Display for Expr<'a> {
}
write!(f, " END")?;
}
Expr::Exists(subquery) => {
Expr::Exists { subquery, .. } => {
write!(f, "EXITS ({subquery})")?;
}
Expr::Subquery(subquery) => {
Expr::Subquery { subquery, .. } => {
write!(f, "({subquery})")?;
}
Expr::MapAccess { expr, accessor } => {
Expr::MapAccess { expr, accessor, .. } => {
write!(f, "{}", expr)?;
match accessor {
MapAccessor::Bracket { key } => write!(f, "[{key}]")?,
Expand Down
2 changes: 1 addition & 1 deletion common/ast/src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ pub enum JoinOperator {

#[derive(Debug, Clone, PartialEq)]
pub enum JoinCondition<'a> {
On(Expr<'a>),
On(Box<Expr<'a>>),
Using(Vec<Identifier<'a>>),
Natural,
None,
Expand Down
4 changes: 2 additions & 2 deletions common/ast/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,13 +695,13 @@ impl<'a> Display for Statement<'a> {
write!(f, " WITH {}", auth_type.to_str())?;
}
if let Some(password) = &auth_option.password {
write!(f, " BY '{password}'", password = password)?;
write!(f, " BY '{password}'")?;
}
}
if !role_options.is_empty() {
write!(f, " WITH")?;
for with_option in role_options {
write!(f, " {with_option}", with_option = with_option)?;
write!(f, " {with_option}")?;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion common/ast/src/parser/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl<'a> DisplayError for Token<'a> {
}
}

impl<'a, 'b> DisplayError for &'b [Token<'a>] {
impl<'a> DisplayError for &'a [Token<'a>] {
type Message = String;

fn display_error(&self, message: String) -> String {
Expand Down
Loading

0 comments on commit 5251ba2

Please sign in to comment.