Skip to content

Commit

Permalink
feat(air-parser): improved canon stream syntax support [fixes VM-293]
Browse files Browse the repository at this point in the history
  • Loading branch information
raftedproc committed Jul 4, 2023
1 parent fcb4c9d commit 90d6f79
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 37 deletions.
61 changes: 51 additions & 10 deletions crates/air-lib/air-parser/src/parser/lexer/call_variable_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ pub(super) fn try_parse_call_variable(
CallVariableParser::try_parse(string_to_parse, start_pos)
}

#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
enum MetTag {
None,
Stream,
StreamMap,
Canon,
CanonStream,
}

Expand Down Expand Up @@ -175,7 +176,10 @@ impl<'input> CallVariableParser<'input> {
}

fn try_parse_as_variable(&mut self) -> LexerResult<()> {
if self.try_parse_as_stream_start()? || self.try_parse_as_json_path_start()? {
if self.try_parse_as_canon()?
|| self.try_parse_as_tagged_token()?
|| self.try_parse_as_json_path_start()?
{
return Ok(());
} else if self.is_json_path_started() {
self.try_parse_as_json_path()?;
Expand All @@ -186,15 +190,34 @@ impl<'input> CallVariableParser<'input> {
Ok(())
}

fn try_parse_as_stream_start(&mut self) -> LexerResult<bool> {
let stream_tag = MetTag::from_tag(self.current_char());
if self.current_offset() == 0 && stream_tag.is_tag() {
fn try_parse_as_tagged_token(&mut self) -> LexerResult<bool> {
let tag = MetTag::from_tag(self.current_char());
if self.current_offset() == 0 && tag.is_tag() {
if self.string_to_parse.len() == 1 {
let error_pos = self.pos_in_string_to_parse();
return Err(LexerError::empty_stream_name(error_pos..error_pos));
return Err(LexerError::empty_tagged_name(error_pos..error_pos));
}

self.state.met_tag = tag;
return Ok(true);
}

Ok(false)
}

fn try_parse_as_canon(&mut self) -> LexerResult<bool> {
let tag = if self.state.met_tag.is_canon() {
self.state.met_tag.from_canon_type(self.current_char())
} else {
self.state.met_tag
};
if self.current_offset() == 1 && tag.is_canon_stream() {
if self.string_to_parse.len() == 2 && tag.is_tag() {
let error_pos = self.pos_in_string_to_parse();
return Err(LexerError::empty_canon_name(error_pos..error_pos));
}

self.state.met_tag = stream_tag;
self.state.met_tag = tag;
return Ok(true);
}

Expand Down Expand Up @@ -238,6 +261,9 @@ impl<'input> CallVariableParser<'input> {
return Err(LexerError::leading_dot(
self.start_pos..self.pos_in_string_to_parse(),
));
} else if self.state.met_tag.is_tag() && self.current_offset() <= 2 {
let prev_pos = self.pos_in_string_to_parse() - 1;
return Err(LexerError::empty_canon_name(prev_pos..prev_pos));
}
self.state.first_dot_met_pos = Some(self.current_offset());
return Ok(true);
Expand Down Expand Up @@ -288,7 +314,7 @@ impl<'input> CallVariableParser<'input> {
name,
position: self.start_pos,
},
MetTag::CanonStream => Token::CanonStream {
MetTag::CanonStream | MetTag::Canon => Token::CanonStream {
name,
position: self.start_pos,
},
Expand All @@ -311,7 +337,7 @@ impl<'input> CallVariableParser<'input> {
lambda,
position: self.start_pos,
},
MetTag::CanonStream => Token::CanonStreamWithLambda {
MetTag::CanonStream | MetTag::Canon => Token::CanonStreamWithLambda {
name,
lambda,
position: self.start_pos,
Expand Down Expand Up @@ -387,12 +413,27 @@ impl MetTag {
fn from_tag(tag: char) -> Self {
match tag {
'$' => Self::Stream,
'#' => Self::CanonStream,
'#' => Self::Canon,
'%' => Self::StreamMap,
_ => Self::None,
}
}

fn from_canon_type(&self, tag: char) -> Self {

Check failure on line 422 in crates/air-lib/air-parser/src/parser/lexer/call_variable_parser.rs

View workflow job for this annotation

GitHub Actions / aquavm / cargo nextest

methods called `from_*` usually take no `self`
match tag {
'$' => Self::CanonStream,
_ => self.to_owned(),
}
}

fn is_canon(&self) -> bool {
matches!(self, Self::Canon)
}

fn is_canon_stream(&self) -> bool {
matches!(self, Self::CanonStream)
}

fn is_tag(&self) -> bool {
!matches!(self, Self::None)
}
Expand Down
18 changes: 13 additions & 5 deletions crates/air-lib/air-parser/src/parser/lexer/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ pub enum LexerError {
#[error("only alphanumeric, '_', and '-' characters are allowed in this position")]
IsNotAlphanumeric(Span),

#[error("a stream name should be non empty")]
EmptyStreamName(Span),
#[error("a tagged name should be non empty")]
EmptyTaggedName(Span),

#[error("a canon name should be non empty")]
EmptyCanonName(Span),

#[error("this variable or constant shouldn't have empty name")]
EmptyVariableOrConst(Span),
Expand Down Expand Up @@ -75,7 +78,8 @@ impl LexerError {
Self::UnclosedQuote(span) => span,
Self::EmptyString(span) => span,
Self::IsNotAlphanumeric(span) => span,
Self::EmptyStreamName(span) => span,
Self::EmptyTaggedName(span) => span,
Self::EmptyCanonName(span) => span,
Self::EmptyVariableOrConst(span) => span,
Self::InvalidLambda(span) => span,
Self::UnallowedCharInNumber(span) => span,
Expand All @@ -102,8 +106,12 @@ impl LexerError {
Self::IsNotAlphanumeric(range.into())
}

pub fn empty_stream_name(range: Range<AirPos>) -> Self {
Self::EmptyStreamName(range.into())
pub fn empty_tagged_name(range: Range<AirPos>) -> Self {
Self::EmptyTaggedName(range.into())
}

pub fn empty_canon_name(range: Range<AirPos>) -> Self {
Self::EmptyCanonName(range.into())
}

pub fn empty_variable_or_const(range: Range<AirPos>) -> Self {
Expand Down
73 changes: 51 additions & 22 deletions crates/air-lib/air-parser/src/parser/lexer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,37 +217,66 @@ fn stream_map() {

#[test]
fn canon_stream() {
const CANON_STREAM: &str = "#stream____asdasd";
for canon_stream_name in vec!["#stream____asdasd", "#$stream____asdasd"] {
lexer_test(
canon_stream_name,
Single(Ok((
0.into(),
Token::CanonStream {
name: canon_stream_name,
position: 0.into(),
},
canon_stream_name.len().into(),
))),
);
}

let cannon_stream_name = "#s$stream____asdasd";
lexer_test(
CANON_STREAM,
Single(Ok((
0.into(),
Token::CanonStream {
name: CANON_STREAM,
position: 0.into(),
},
CANON_STREAM.len().into(),
))),
cannon_stream_name,
Single(Err(LexerError::is_not_alphanumeric(2.into()..2.into()))),
);

let cannon_stream_name = "#";
lexer_test(
cannon_stream_name,
Single(Err(LexerError::empty_tagged_name(0.into()..0.into()))),
);
}

#[test]
fn canon_stream_with_functor() {
let canon_stream_name = "#canon_stream";
let canon_stream_with_functor: String = f!("{canon_stream_name}.length");
for canon_stream_name in vec!["#canon_stream", "#$canon_stream"] {
let canon_stream_with_functor: String = f!("{canon_stream_name}.length");

lexer_test(
&canon_stream_with_functor,
Single(Ok((
0.into(),
Token::CanonStreamWithLambda {
name: canon_stream_name,
lambda: LambdaAST::Functor(Functor::Length),
position: 0.into(),
},
canon_stream_with_functor.len().into(),
))),
);
}

let cannon_stream_name = "#s$stream____asdasd.length";
lexer_test(
&canon_stream_with_functor,
Single(Ok((
0.into(),
Token::CanonStreamWithLambda {
name: canon_stream_name,
lambda: LambdaAST::Functor(Functor::Length),
position: 0.into(),
},
canon_stream_with_functor.len().into(),
))),
cannon_stream_name,
Single(Err(LexerError::is_not_alphanumeric(2.into()..2.into()))),
);
let cannon_stream_name = "#.length";
lexer_test(
cannon_stream_name,
Single(Err(LexerError::empty_canon_name(0.into()..0.into()))),
);
let cannon_stream_name = "#$.length";
lexer_test(
cannon_stream_name,
Single(Err(LexerError::empty_canon_name(1.into()..1.into()))),
);
}

Expand Down

0 comments on commit 90d6f79

Please sign in to comment.