Skip to content

Commit 2daeb7a

Browse files
authored
Add || (concat) infix operator (#113)
1 parent c2ab1cf commit 2daeb7a

File tree

3 files changed

+76
-53
lines changed

3 files changed

+76
-53
lines changed

partiql-parser/src/lexer.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ pub enum Token<'input> {
457457
Caret,
458458
#[token(".")]
459459
Period,
460+
#[token("||")]
461+
DblPipe,
460462

461463
// unquoted identifiers
462464
#[regex("[a-zA-Z_$][a-zA-Z0-9_$]*", |lex| lex.slice())]
@@ -628,14 +630,15 @@ impl<'input> fmt::Display for Token<'input> {
628630
Token::Slash => write!(f, "/"),
629631
Token::Caret => write!(f, "^"),
630632
Token::Period => write!(f, "."),
631-
Token::Identifier(_) => write!(f, "<IDENTIFIER>"),
632-
Token::AtIdentifier(_) => write!(f, "<@IDENTIFIER>"),
633-
Token::Int(_) => write!(f, "<INT>"),
634-
Token::ExpReal(_) => write!(f, "<REAL>"),
635-
Token::Real(_) => write!(f, "<REAL>"),
636-
Token::String(_) => write!(f, "<STRING>"),
633+
Token::DblPipe => write!(f, "||"),
634+
Token::Identifier(id) => write!(f, "<{}:IDENT>", id),
635+
Token::AtIdentifier(id) => write!(f, "<{}:@IDENT>", id),
636+
Token::Int(txt) => write!(f, "<{}:INT>", txt),
637+
Token::ExpReal(txt) => write!(f, "<{}:REAL>", txt),
638+
Token::Real(txt) => write!(f, "<{}:REAL>", txt),
639+
Token::String(txt) => write!(f, "<{}:STRING>", txt),
637640
Token::EmbeddedIonQuote => write!(f, "<ION>"),
638-
Token::Ion(_) => write!(f, "<ION>"),
641+
Token::Ion(txt) => write!(f, "<{}:ION>", txt),
639642

640643
Token::All
641644
| Token::Asc
@@ -704,8 +707,9 @@ mod tests {
704707

705708
#[test]
706709
fn display() -> Result<(), ParseError<'static, BytePosition>> {
707-
let symbols = "( [ { } ] ) << >> ; , < > <= >= != <> = == - + * % / ^ . : --foo /*block*/";
708-
let primitives = "ident @ident";
710+
let symbols =
711+
"( [ { } ] ) << >> ; , < > <= >= != <> = == - + * % / ^ . || : --foo /*block*/";
712+
let primitives = "ident @atident";
709713
let keywords =
710714
"WiTH Where Value uSiNg Unpivot UNION True Select right Preserve pivoT Outer Order Or \
711715
On Offset Nulls Null Not Natural Missing Limit Like Left Lateral Last Join \
@@ -727,8 +731,8 @@ mod tests {
727731
")", "UNION", "<<", "TRUE", ">>", "SELECT", ";", "RIGHT", ",", "PRESERVE", "<",
728732
"PIVOT", ">", "OUTER", "<=", "ORDER", ">=", "OR", "!=", "ON", "<>", "OFFSET",
729733
"=", "NULLS", "==", "NULL", "-", "NOT", "+", "NATURAL", "*", "MISSING", "%",
730-
"LIMIT", "/", "LIKE", "^", "LEFT", ".", "LATERAL", ":", "LAST", "--", "JOIN",
731-
"/**/", "INTERSECT", "<IDENTIFIER>", "IS", "<@IDENTIFIER>", "INNER", "IN",
734+
"LIMIT", "/", "LIKE", "^", "LEFT", ".", "LATERAL", "||", "LAST", ":", "JOIN",
735+
"--", "INTERSECT", "/**/", "IS", "<ident:IDENT>", "INNER", "<atident:@IDENT>", "IN",
732736
"HAVING", "GROUP", "FROM", "FULL", "FIRST", "FALSE", "EXCEPT", "ESCAPE", "DESC",
733737
"CROSS", "BY", "BETWEEN", "AT", "AS", "AND", "ASC", "ALL"
734738
];

partiql-parser/src/parse/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ mod tests {
284284
fn or_and() {
285285
parse!(r#"t1.super OR test(t2.name, t1.name) AND t1.id = t2.id"#)
286286
}
287+
288+
#[test]
289+
fn infix() {
290+
parse!(r#"1 + -2 * +3 % 4^5 / 6 - 7 <= 3.14 AND 'foo' || 'bar' LIKE '%oba%'"#)
291+
}
287292
}
288293

289294
mod sfw {

partiql-parser/src/parse/partiql.lalrpop

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ SelectClause: ast::Projection = {
105105
Projection: ast::ProjectItem = {
106106
<expr:ExprQuery>
107107
=> ast::ProjectItem::ProjectExpr( ast::ProjectExpr{ expr, as_alias: None } ),
108-
<expr:ExprQuery> "AS" <ident:"Identifier"> => {
108+
<expr:ExprQuery> "AS"? <ident:"Identifier"> => {
109109
let as_alias = Some( ast::SymbolPrimitive{ value:ident.to_owned()} );
110110
ast::ProjectItem::ProjectExpr( ast::ProjectExpr{ expr, as_alias } )
111111
},
@@ -359,68 +359,69 @@ OffsetByClause: Box<ast::Expr> = { "OFFSET" <ExprQuery> }
359359
// | 4 | ^ | left | exponentiation |
360360
// | 5 | * / % | left | multiplication, division, modulo |
361361
// | 6 | + - | left | addition, subtraction |
362-
// | 7 | BETWEEN IN LIKE | | range/set/pattern compare |
363-
// | 8 | < > <= >= | | comparison operators |
364-
// | 9 | = <> != | | equality operators |
365-
// | 10 | IS | | IS [NOT] NULL |
366-
// | 11 | NOT | right | logical negate |
367-
// | 12 | AND | left | logical conjuct |
368-
// | 13 | OR | left | logical disjunct |
362+
// | 7 | <other> | left | other operators, e.g., `||`
363+
// | 8 | BETWEEN IN LIKE | | range/set/pattern compare |
364+
// | 9 | < > <= >= | | comparison operators |
365+
// | 10 | = <> != | | equality operators |
366+
// | 11 | IS | | IS [NOT] NULL |
367+
// | 12 | NOT | right | logical negate |
368+
// | 13 | AND | left | logical conjuct |
369+
// | 14 | OR | left | logical disjunct |
369370
// |-------+-------------------+---------------+------------------------------------|
370371
//
371372
// See https://en.wikipedia.org/wiki/Order_of_operations#Programming_languages
372373
// See https://en.wikipedia.org/wiki/Order_of_operations#Special_cases
373374

374375

375376
pub ExprQuery: Box<ast::Expr> = {
376-
<e:ExprPrecedence13> => Box::new(e)
377+
<e:ExprPrecedence14> => Box::new(e)
377378
}
378379

379-
ExprPrecedence13: ast::Expr = {
380-
<lo:@L> <l:ExprPrecedence13> "OR" <r:ExprPrecedence12> <hi:@R> =>
380+
ExprPrecedence14: ast::Expr = {
381+
<lo:@L> <l:ExprPrecedence14> "OR" <r:ExprPrecedence13> <hi:@R> =>
381382
ast::Expr{ kind: ast::ExprKind::BinOp(
382383
ast::BinOp {
383384
kind: ast::BinOpKind::Or,
384385
lhs: Box::new(l),
385386
rhs: Box::new(r),
386387
}.ast(lo..hi)
387388
)},
388-
<ExprPrecedence12>,
389+
<ExprPrecedence13>,
389390
}
390391

391-
ExprPrecedence12: ast::Expr = {
392-
<lo:@L> <l:ExprPrecedence12> "AND" <r:ExprPrecedence11> <hi:@R> =>
392+
ExprPrecedence13: ast::Expr = {
393+
<lo:@L> <l:ExprPrecedence13> "AND" <r:ExprPrecedence12> <hi:@R> =>
393394
ast::Expr{ kind: ast::ExprKind::BinOp(
394395
ast::BinOp {
395396
kind: ast::BinOpKind::And,
396397
lhs: Box::new(l),
397398
rhs: Box::new(r),
398399
}.ast(lo..hi)
399400
)},
400-
<ExprPrecedence11>,
401+
<ExprPrecedence12>,
401402
}
402403

403-
ExprPrecedence11: ast::Expr = {
404-
<lo:@L> "NOT" <r:ExprPrecedence11> <hi:@R> =>
404+
ExprPrecedence12: ast::Expr = {
405+
<lo:@L> "NOT" <r:ExprPrecedence12> <hi:@R> =>
405406
ast::Expr{ kind: ast::ExprKind::UniOp(
406407
ast::UniOp {
407408
kind: ast::UniOpKind::Not,
408409
expr: Box::new(r),
409410
}.ast(lo..hi)
410411
)},
411-
<ExprPrecedence10>,
412+
<ExprPrecedence11>,
412413
}
413414

414-
ExprPrecedence10: ast::Expr = {
415-
<lo:@L> <l:ExprPrecedence10> "IS" <r:ExprPrecedence09> <hi:@R> =>
415+
ExprPrecedence11: ast::Expr = {
416+
<lo:@L> <l:ExprPrecedence11> "IS" <r:ExprPrecedence10> <hi:@R> =>
416417
ast::Expr{ kind: ast::ExprKind::BinOp(
417418
ast::BinOp {
418419
kind: ast::BinOpKind::Is,
419420
lhs: Box::new(l),
420421
rhs: Box::new(r),
421422
}.ast(lo..hi)
422423
)},
423-
<lo:@L> <l:ExprPrecedence10> "IS" "NOT" <r:ExprPrecedence09> <hi:@R> => {
424+
<lo:@L> <l:ExprPrecedence11> "IS" "NOT" <r:ExprPrecedence10> <hi:@R> => {
424425
let is = ast::Expr{ kind: ast::ExprKind::BinOp(
425426
ast::BinOp {
426427
kind: ast::BinOpKind::Is,
@@ -435,77 +436,77 @@ ExprPrecedence10: ast::Expr = {
435436
}.ast(lo..hi)
436437
)}
437438
},
438-
<ExprPrecedence09>
439+
<ExprPrecedence10>
439440
}
440441

441-
ExprPrecedence09: ast::Expr = {
442-
<lo:@L> <l:ExprPrecedence09> "=" <r:ExprPrecedence08> <hi:@R> =>
442+
ExprPrecedence10: ast::Expr = {
443+
<lo:@L> <l:ExprPrecedence10> "=" <r:ExprPrecedence09> <hi:@R> =>
443444
ast::Expr{ kind: ast::ExprKind::BinOp(
444445
ast::BinOp {
445446
kind: ast::BinOpKind::Eq,
446447
lhs: Box::new(l),
447448
rhs: Box::new(r),
448449
}.ast(lo..hi)
449450
)},
450-
<lo:@L> <l:ExprPrecedence09> "!=" <r:ExprPrecedence08> <hi:@R> =>
451+
<lo:@L> <l:ExprPrecedence10> "!=" <r:ExprPrecedence09> <hi:@R> =>
451452
ast::Expr{ kind: ast::ExprKind::BinOp(
452453
ast::BinOp {
453454
kind: ast::BinOpKind::Ne,
454455
lhs: Box::new(l),
455456
rhs: Box::new(r),
456457
}.ast(lo..hi)
457458
)},
458-
<lo:@L> <l:ExprPrecedence09> "<>" <r:ExprPrecedence08> <hi:@R> =>
459+
<lo:@L> <l:ExprPrecedence10> "<>" <r:ExprPrecedence09> <hi:@R> =>
459460
ast::Expr{ kind: ast::ExprKind::BinOp(
460461
ast::BinOp {
461462
kind: ast::BinOpKind::Ne,
462463
lhs: Box::new(l),
463464
rhs: Box::new(r),
464465
}.ast(lo..hi)
465466
)},
466-
<ExprPrecedence08>,
467+
<ExprPrecedence09>,
467468
}
468469

469-
ExprPrecedence08: ast::Expr = {
470-
<lo:@L> <l:ExprPrecedence08> "<" <r:ExprPrecedence07> <hi:@R> =>
470+
ExprPrecedence09: ast::Expr = {
471+
<lo:@L> <l:ExprPrecedence08> "<" <r:ExprPrecedence08> <hi:@R> =>
471472
ast::Expr{ kind: ast::ExprKind::BinOp(
472473
ast::BinOp {
473474
kind: ast::BinOpKind::Lt,
474475
lhs: Box::new(l),
475476
rhs: Box::new(r),
476477
}.ast(lo..hi)
477478
)},
478-
<lo:@L> <l:ExprPrecedence08> ">" <r:ExprPrecedence07> <hi:@R> =>
479+
<lo:@L> <l:ExprPrecedence08> ">" <r:ExprPrecedence08> <hi:@R> =>
479480
ast::Expr{ kind: ast::ExprKind::BinOp(
480481
ast::BinOp {
481482
kind: ast::BinOpKind::Gt,
482483
lhs: Box::new(l),
483484
rhs: Box::new(r),
484485
}.ast(lo..hi)
485486
)},
486-
<lo:@L> <l:ExprPrecedence08> "<=" <r:ExprPrecedence07> <hi:@R> =>
487+
<lo:@L> <l:ExprPrecedence08> "<=" <r:ExprPrecedence08> <hi:@R> =>
487488
ast::Expr{ kind: ast::ExprKind::BinOp(
488489
ast::BinOp {
489490
kind: ast::BinOpKind::Lte,
490491
lhs: Box::new(l),
491492
rhs: Box::new(r),
492493
}.ast(lo..hi)
493494
)},
494-
<lo:@L> <l:ExprPrecedence08> ">=" <r:ExprPrecedence07> <hi:@R> =>
495+
<lo:@L> <l:ExprPrecedence08> ">=" <r:ExprPrecedence08> <hi:@R> =>
495496
ast::Expr{ kind: ast::ExprKind::BinOp(
496497
ast::BinOp {
497498
kind: ast::BinOpKind::Gte,
498499
lhs: Box::new(l),
499500
rhs: Box::new(r),
500501
}.ast(lo..hi)
501502
)},
502-
<ExprPrecedence07>,
503+
<ExprPrecedence08>,
503504
}
504505

505-
ExprPrecedence07: ast::Expr = {
506-
<lo:@L> <value:ExprPrecedence07> "BETWEEN" <from:ExprPrecedence06> "AND" <to:ExprPrecedence06> <hi:@R> =>
506+
ExprPrecedence08: ast::Expr = {
507+
<lo:@L> <value:ExprPrecedence08> "BETWEEN" <from:ExprPrecedence07> "AND" <to:ExprPrecedence07> <hi:@R> =>
507508
ast::Expr{ kind: ast::ExprKind::Between( ast::Between{ value: Box::new(value), from: Box::new(from), to: Box::new(to) }.ast(lo..hi) ) },
508-
<lo:@L> <value:ExprPrecedence07> "NOT" "BETWEEN" <from:ExprPrecedence06> "AND" <to:ExprPrecedence06> <hi:@R> => {
509+
<lo:@L> <value:ExprPrecedence08> "NOT" "BETWEEN" <from:ExprPrecedence07> "AND" <to:ExprPrecedence07> <hi:@R> => {
509510
let between = ast::Expr{ kind: ast::ExprKind::Between( ast::Between{ value: Box::new(value), from: Box::new(from), to: Box::new(to) }.ast(lo..hi) ) };
510511
ast::Expr{ kind: ast::ExprKind::UniOp(
511512
ast::UniOp {
@@ -514,9 +515,9 @@ ExprPrecedence07: ast::Expr = {
514515
}.ast(lo..hi)
515516
)}
516517
},
517-
<lo:@L> <value:ExprPrecedence07> "LIKE" <pattern:ExprPrecedence06> <escape:LikeEscape?> <hi:@R> =>
518+
<lo:@L> <value:ExprPrecedence08> "LIKE" <pattern:ExprPrecedence07> <escape:LikeEscape?> <hi:@R> =>
518519
ast::Expr{ kind: ast::ExprKind::Like( ast::Like{ value: Box::new(value), pattern: Box::new(pattern), escape }.ast(lo..hi) ) },
519-
<lo:@L> <value:ExprPrecedence07> "NOT" "LIKE" <pattern:ExprPrecedence06> <escape:LikeEscape?> <hi:@R> => {
520+
<lo:@L> <value:ExprPrecedence08> "NOT" "LIKE" <pattern:ExprPrecedence07> <escape:LikeEscape?> <hi:@R> => {
520521
let like = ast::Expr{ kind: ast::ExprKind::Like( ast::Like{ value: Box::new(value), pattern: Box::new(pattern), escape }.ast(lo..hi) ) };
521522
ast::Expr{ kind: ast::ExprKind::UniOp(
522523
ast::UniOp {
@@ -525,9 +526,9 @@ ExprPrecedence07: ast::Expr = {
525526
}.ast(lo..hi)
526527
)}
527528
},
528-
<lo:@L> <l:ExprPrecedence07> "IN" <r:ExprPrecedence06> <hi:@R> =>
529+
<lo:@L> <l:ExprPrecedence08> "IN" <r:ExprPrecedence07> <hi:@R> =>
529530
ast::Expr{ kind: ast::ExprKind::In( ast::In{ operands: vec![Box::new(l),Box::new(r)] }.ast(lo..hi) ) },
530-
<lo:@L> <l:ExprPrecedence07> "NOT" "IN" <r:ExprPrecedence06> <hi:@R> => {
531+
<lo:@L> <l:ExprPrecedence08> "NOT" "IN" <r:ExprPrecedence07> <hi:@R> => {
531532
let in_expr = ast::Expr{ kind: ast::ExprKind::In( ast::In{ operands: vec![Box::new(l),Box::new(r)] }.ast(lo..hi) ) };
532533
ast::Expr{ kind: ast::ExprKind::UniOp(
533534
ast::UniOp {
@@ -536,11 +537,23 @@ ExprPrecedence07: ast::Expr = {
536537
}.ast(lo..hi)
537538
)}
538539
},
539-
<ExprPrecedence06>,
540+
<ExprPrecedence07>,
540541
}
541542
#[inline]
542543
LikeEscape: Box<ast::Expr> = {
543-
"ESCAPE" <e:ExprPrecedence06> => Box::new(e)
544+
"ESCAPE" <e:ExprPrecedence07> => Box::new(e)
545+
}
546+
547+
ExprPrecedence07: ast::Expr = {
548+
<lo:@L> <l:ExprPrecedence07> "||" <r:ExprPrecedence06> <hi:@R> =>
549+
ast::Expr{ kind: ast::ExprKind::BinOp(
550+
ast::BinOp {
551+
kind: ast::BinOpKind::Concat,
552+
lhs: Box::new(l),
553+
rhs: Box::new(r),
554+
}.ast(lo..hi)
555+
)},
556+
<ExprPrecedence06>,
544557
}
545558

546559
ExprPrecedence06: ast::Expr = {
@@ -822,6 +835,7 @@ extern {
822835
"/" => lexer::Token::Slash,
823836
"%" => lexer::Token::Percent,
824837
"^" => lexer::Token::Caret,
838+
"||" => lexer::Token::DblPipe,
825839

826840
"=" => lexer::Token::Equal,
827841
"==" => lexer::Token::EqualEqual,

0 commit comments

Comments
 (0)