Support scalars in lambda (#192)

This commit is contained in:
Mike Voronov
2021-12-15 12:46:09 +03:00
committed by GitHub
parent 9f91d3161f
commit 1d8182d497
37 changed files with 1073 additions and 318 deletions

View File

@ -27,7 +27,7 @@
mod parser;
pub use parser::parse;
pub use parser::AlgebraLexer;
pub use parser::AccessorsLexer;
pub use parser::LambdaParser;
pub use parser::LambdaParserError;
pub use parser::LexerError;

View File

@ -26,7 +26,7 @@ pub enum LambdaParserError<'input> {
#[error(transparent)]
LexerError(#[from] LexerError),
#[error("provided lambda expression doesn't contain any algebras")]
#[error("provided lambda expression doesn't contain any accessor")]
EmptyLambda,
#[error("{0:?}")]

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
use super::lexer::AlgebraLexer;
use super::lexer::AccessorsLexer;
use super::va_lambda;
use super::LambdaParserError;
use super::LambdaParserResult;
@ -31,22 +31,22 @@ thread_local!(static PARSER: LambdaParser = LambdaParser::new());
pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
PARSER.with(|parser| {
let mut errors = Vec::new();
let lexer = AlgebraLexer::new(lambda);
let lexer = AccessorsLexer::new(lambda);
let result = parser.parse(lambda, &mut errors, lexer);
match result {
Ok(algebras) if errors.is_empty() => try_to_lambda(algebras),
Ok(accessors) if errors.is_empty() => try_to_lambda(accessors),
Ok(_) => Err(errors.into()),
Err(e) => Err(e.into()),
}
})
}
fn try_to_lambda(algebras: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
if algebras.is_empty() {
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
if accessors.is_empty() {
return Err(LambdaParserError::EmptyLambda);
}
let ast = unsafe { LambdaAST::new_unchecked(algebras) };
let ast = unsafe { LambdaAST::new_unchecked(accessors) };
Ok(ast)
}

View File

@ -25,12 +25,12 @@ const ARRAY_IDX_BASE: u32 = 10;
pub type Spanned<Token, Loc, Error> = Result<(Loc, Token, Loc), Error>;
pub struct AlgebraLexer<'input> {
pub struct AccessorsLexer<'input> {
input: &'input str,
chars: Peekable<CharIndices<'input>>,
}
impl<'input> Iterator for AlgebraLexer<'input> {
impl<'input> Iterator for AccessorsLexer<'input> {
type Item = Spanned<Token<'input>, usize, LexerError>;
fn next(&mut self) -> Option<Self::Item> {
@ -38,7 +38,7 @@ impl<'input> Iterator for AlgebraLexer<'input> {
}
}
impl<'input> AlgebraLexer<'input> {
impl<'input> AccessorsLexer<'input> {
pub fn new(input: &'input str) -> Self {
Self {
input,
@ -71,7 +71,11 @@ impl<'input> AlgebraLexer<'input> {
.parse::<u32>()
.map_err(|e| LexerError::ParseIntError(start_pos, start_pos + array_idx.len(), e))
{
Ok(idx) => Ok((start_pos, Token::ArrayIdx(idx), start_pos + array_idx.len())),
Ok(idx) => Ok((
start_pos,
Token::NumberAccessor(idx),
start_pos + array_idx.len(),
)),
Err(e) => Err(e),
}
}
@ -84,7 +88,7 @@ impl<'input> AlgebraLexer<'input> {
Ok((
start_pos,
Token::FieldName(field_name),
Token::StringAccessor(field_name),
start_pos + field_name.len(),
))
}

View File

@ -20,7 +20,7 @@ use std::num::ParseIntError;
#[derive(ThisError, Debug, Clone, PartialEq, Eq)]
pub enum LexerError {
#[error("unexpected symbol for value algebra")]
#[error("unexpected symbol for value accessor")]
UnexpectedSymbol(usize, usize),
#[error("{2}")]

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
mod algebra_lexer;
mod accessors_lexer;
mod errors;
mod token;
mod utils;
@ -22,7 +22,7 @@ mod utils;
#[cfg(test)]
mod tests;
pub use algebra_lexer::AlgebraLexer;
pub use accessors_lexer::AccessorsLexer;
pub use errors::LexerError;
pub use token::Token;

View File

@ -14,13 +14,13 @@
* limitations under the License.
*/
use super::algebra_lexer::Spanned;
use super::AlgebraLexer;
use super::accessors_lexer::Spanned;
use super::AccessorsLexer;
use super::LexerError;
use super::Token;
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
let lexer = AlgebraLexer::new(input);
let lexer = AccessorsLexer::new(input);
lexer.collect()
}
@ -32,7 +32,7 @@ fn array_access() {
let expected = vec![
Spanned::Ok((0, Token::Selector, 1)),
Spanned::Ok((1, Token::OpenSquareBracket, 2)),
Spanned::Ok((2, Token::ArrayIdx(0), 3)),
Spanned::Ok((2, Token::NumberAccessor(0), 3)),
Spanned::Ok((3, Token::CloseSquareBracket, 4)),
];
assert_eq!(actual, expected);
@ -46,7 +46,7 @@ fn field_access() {
let actual = run_lexer(&field_access);
let expected = vec![
Spanned::Ok((0, Token::Selector, 1)),
Spanned::Ok((1, Token::FieldName(field_name), 1 + field_name.len())),
Spanned::Ok((1, Token::StringAccessor(field_name), 1 + field_name.len())),
];
assert_eq!(actual, expected);
}

View File

@ -25,8 +25,8 @@ pub enum Token<'input> {
OpenSquareBracket,
CloseSquareBracket,
ArrayIdx(u32),
FieldName(&'input str),
NumberAccessor(u32),
StringAccessor(&'input str),
// !
FlatteningSign,

View File

@ -31,7 +31,7 @@ pub type LambdaParserResult<'input, T> = std::result::Result<T, LambdaParserErro
pub use errors::LambdaParserError;
pub use lambda_parser::parse;
pub use lexer::AlgebraLexer;
pub use lexer::AccessorsLexer;
pub use lexer::LexerError;
pub use va_lambda::LambdaParser;

View File

@ -22,7 +22,7 @@ thread_local!(static TEST_PARSER: LambdaParser = LambdaParser::new());
fn parse(source_code: &str) -> Vec<ValueAccessor<'_>> {
TEST_PARSER.with(|parser| {
let mut errors = Vec::new();
let lexer = crate::parser::AlgebraLexer::new(source_code);
let lexer = crate::parser::AccessorsLexer::new(source_code);
parser
.parse(source_code, &mut errors, lexer)
.expect("parsing should be successful")
@ -35,7 +35,7 @@ fn field_access() {
let lambda = format!(".{}", field_name);
let actual = parse(&lambda);
let expected = vec![ValueAccessor::FieldAccess { field_name }];
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
assert_eq!(actual, expected);
}
@ -45,7 +45,7 @@ fn field_access_with_flattening() {
let lambda = format!(".{}!", field_name);
let actual = parse(&lambda);
let expected = vec![ValueAccessor::FieldAccess { field_name }];
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
assert_eq!(actual, expected);
}
@ -69,6 +69,26 @@ fn array_access_with_flattening() {
assert_eq!(actual, expected);
}
#[test]
fn scalar_access() {
let scalar_name = "some_field_name";
let lambda = format!(".[{}]", scalar_name);
let actual = parse(&lambda);
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
assert_eq!(actual, expected);
}
#[test]
fn scalar_access_with_flattening() {
let scalar_name = "some_scalar_name";
let lambda = format!(".[{}]!", scalar_name);
let actual = parse(&lambda);
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
assert_eq!(actual, expected);
}
#[test]
fn field_array_access() {
let field_name = "some_field_name";
@ -77,7 +97,35 @@ fn field_array_access() {
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::FieldAccess { field_name },
ValueAccessor::FieldAccessByName { field_name },
ValueAccessor::ArrayAccess { idx },
];
assert_eq!(actual, expected);
}
#[test]
fn field_scalar_access() {
let field_name = "some_field_name";
let scalar_name = "some_scalar_name";
let lambda = format!(".{}.[{}]", field_name, scalar_name);
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::FieldAccessByName { field_name },
ValueAccessor::FieldAccessByScalar { scalar_name },
];
assert_eq!(actual, expected);
}
#[test]
fn scalar_array_access() {
let scalar_name = "some_scalar_name";
let idx = 1;
let lambda = format!(".[{}].[{}]", scalar_name, idx);
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::FieldAccessByScalar { scalar_name },
ValueAccessor::ArrayAccess { idx },
];
assert_eq!(actual, expected);
@ -91,7 +139,7 @@ fn field_array_access_without_dot() {
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::FieldAccess { field_name },
ValueAccessor::FieldAccessByName { field_name },
ValueAccessor::ArrayAccess { idx },
];
assert_eq!(actual, expected);
@ -106,7 +154,21 @@ fn array_field_access() {
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::ArrayAccess { idx },
ValueAccessor::FieldAccess { field_name },
ValueAccessor::FieldAccessByName { field_name },
];
assert_eq!(actual, expected);
}
#[test]
fn array_scalar_access() {
let scalar_name = "some_scalar_name";
let idx = 1;
let lambda = format!(".[{}].[{}]", idx, scalar_name);
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::ArrayAccess { idx },
ValueAccessor::FieldAccessByScalar { scalar_name },
];
assert_eq!(actual, expected);
}
@ -122,11 +184,44 @@ fn many_array_field_access() {
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::ArrayAccess { idx: idx_1 },
ValueAccessor::FieldAccess {
ValueAccessor::FieldAccessByName {
field_name: field_name_1,
},
ValueAccessor::ArrayAccess { idx: idx_2 },
ValueAccessor::FieldAccess {
ValueAccessor::FieldAccessByName {
field_name: field_name_2,
},
];
assert_eq!(actual, expected);
}
#[test]
fn many_array_field_scalar_access() {
let field_name_1 = "some_field_name_1";
let field_name_2 = "some_field_name_2";
let idx_1 = 1;
let idx_2 = u32::MAX;
let scalar_name_1 = "some_scalar_name_1";
let scalar_name_2 = "some_scalar_name_2";
let lambda = format!(
".[{}].[{}].{}.[{}].[{}].{}",
idx_1, scalar_name_1, field_name_1, idx_2, scalar_name_2, field_name_2
);
let actual = parse(&lambda);
let expected = vec![
ValueAccessor::ArrayAccess { idx: idx_1 },
ValueAccessor::FieldAccessByScalar {
scalar_name: scalar_name_1,
},
ValueAccessor::FieldAccessByName {
field_name: field_name_1,
},
ValueAccessor::ArrayAccess { idx: idx_2 },
ValueAccessor::FieldAccessByScalar {
scalar_name: scalar_name_2,
},
ValueAccessor::FieldAccessByName {
field_name: field_name_2,
},
];

View File

@ -1,4 +1,4 @@
use crate::ValueAlgebra;
use crate::ValueAccessor;
use crate::parser::lexer::LexerError;
use crate::parser::lexer::Token;
@ -7,18 +7,22 @@ use lalrpop_util::ErrorRecovery;
// the only thing why input matters here is just introducing lifetime for Token
grammar<'err, 'input>(input: &'input str, errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, LexerError>>);
pub Lambda: Vec<ValueAlgebra<'input>> = <ValueAlgebra*> => <>;
pub Lambda: Vec<ValueAccessor<'input>> = <ValueAccessor*> => <>;
ValueAlgebra: ValueAlgebra<'input> = {
<maybe_dot_selector:"."?> "[" <idx: array_idx> "]" <maybe_flatten_sign:"!"?> => {
ValueAlgebra::ArrayAccess { idx }
ValueAccessor: ValueAccessor<'input> = {
<maybe_dot_selector:"."?> "[" <idx: number_accessor> "]" <maybe_flatten_sign:"!"?> => {
ValueAccessor::ArrayAccess { idx }
},
"." <field_name: field_name> <maybe_flatten_sign:"!"?> => {
ValueAlgebra::FieldAccess { field_name }
<maybe_dot_selector:"."?> "[" <scalar_name: string_accessor> "]" <maybe_flatten_sign:"!"?> => {
ValueAccessor::FieldAccessByScalar { scalar_name }
},
! => { errors.push(<>); ValueAlgebra::Error },
"." <field_name: string_accessor> <maybe_flatten_sign:"!"?> => {
ValueAccessor::FieldAccessByName { field_name }
},
! => { errors.push(<>); ValueAccessor::Error },
}
extern {
@ -31,8 +35,8 @@ extern {
"[" => Token::OpenSquareBracket,
"]" => Token::CloseSquareBracket,
array_idx => Token::ArrayIdx(<u32>),
field_name => Token::FieldName(<&'input str>),
number_accessor => Token::NumberAccessor(<u32>),
string_accessor => Token::StringAccessor(<&'input str>),
"!" => Token::FlatteningSign,
}

File diff suppressed because it is too large Load Diff