use crate::dsl::{
expressions::ExprSpan,
tokenizer::{Token, TokenKind},
DslError,
};
pub(super) fn verify_tokens(tokens: Vec<Token>) -> Result<Vec<Token>, DslError> {
verify_brackets(tokens)
.and_then(verify_semicolons)
.and_then(verify_commas)
}
fn verify_brackets(tokens: Vec<Token>) -> Result<Vec<Token>, DslError> {
let mut stack: Vec<&Token> = Vec::new();
const LEFT_BRACKETS: [TokenKind; 3] =
[TokenKind::LeftParen, TokenKind::LeftBracket, TokenKind::LeftBrace];
const RIGHT_BRACKETS: [TokenKind; 3] =
[TokenKind::RightParen, TokenKind::RightBracket, TokenKind::RightBrace];
let rhs = |t: &TokenKind| match t {
TokenKind::LeftParen => TokenKind::RightParen,
TokenKind::LeftBracket => TokenKind::RightBracket,
TokenKind::LeftBrace => TokenKind::RightBrace,
_ => unreachable!(),
};
let bracket_mismatch =
|t: &Token, bracket_type: &'static str| -> Result<Vec<Token>, DslError> {
let bracket = t.text.chars().next().unwrap();
Err(DslError::BracketMismatch {
bracket,
location: ExprSpan::new(t.span.0, t.span.1),
bracket_type,
})
};
for token in &tokens {
if LEFT_BRACKETS.contains(&token.kind) {
stack.push(token);
} else if RIGHT_BRACKETS.contains(&token.kind) {
if let Some(top) = stack.pop() {
if token.kind != rhs(&top.kind) {
return bracket_mismatch(token, "closing");
}
} else {
return bracket_mismatch(token, "closing");
}
}
}
if let Some(trailing) = stack.last() {
return bracket_mismatch(trailing, "opening");
}
Ok(tokens)
}
fn verify_semicolons(tokens: Vec<Token>) -> Result<Vec<Token>, DslError> {
if tokens.is_empty() {
return Ok(tokens);
}
for i in 1..tokens.len() {
if tokens[i].kind == TokenKind::Semicolon && tokens[i - 1].kind == TokenKind::Semicolon {
return Err(DslError::SyntaxError {
message: "Multiple consecutive semicolons".into(),
location: ExprSpan::new(tokens[i].span.0, tokens[i].span.1),
});
}
}
for i in 0..tokens.len() - 1 {
if tokens[i].kind == TokenKind::Keyword && tokens[i].text == "let" {
let mut j = i + 1;
let mut depth = 0;
while j < tokens.len() {
match tokens[j].kind {
TokenKind::LeftParen | TokenKind::LeftBrace | TokenKind::LeftBracket => {
depth += 1;
},
TokenKind::RightParen | TokenKind::RightBrace | TokenKind::RightBracket => {
if depth > 0 {
depth -= 1;
}
},
TokenKind::Semicolon if depth == 0 => break,
_ => {},
}
j += 1;
}
if j == tokens.len() && i < tokens.len() - 2 {
return Err(DslError::MissingSemicolon {
location: ExprSpan::new(tokens[j - 1].span.1, tokens[j - 1].span.1 + 1),
});
}
}
}
Ok(tokens)
}
fn verify_commas(tokens: Vec<Token>) -> Result<Vec<Token>, DslError> {
if tokens.is_empty() {
return Ok(tokens);
}
let mut stack: Vec<(TokenKind, usize)> = Vec::new(); let mut last_token_kind = TokenKind::Whitespace;
for (i, token) in tokens.iter().enumerate() {
match token.kind {
TokenKind::LeftBracket | TokenKind::LeftParen => {
stack.push((token.kind, i));
},
TokenKind::RightBracket | TokenKind::RightParen | TokenKind::RightBrace => {
if let Some((opening, _)) = stack.last() {
let is_matching = matches!(
(opening, token.kind),
(TokenKind::LeftBracket, TokenKind::RightBracket)
| (TokenKind::LeftParen, TokenKind::RightParen)
| (TokenKind::LeftBrace, TokenKind::RightBrace)
);
if is_matching {
stack.pop();
}
}
},
TokenKind::Identifier
| TokenKind::StringLiteral
| TokenKind::IntLiteral
| TokenKind::FloatLiteral
| TokenKind::HexLiteral => {
if !stack.is_empty() {
let (delimiter_kind, last_delimiter_pos) = *stack.last().unwrap();
if delimiter_kind == TokenKind::LeftBracket
|| delimiter_kind == TokenKind::LeftParen
{
if is_expression_token(last_token_kind)
&& !is_opening_delimiter(last_token_kind)
{
if last_delimiter_pos != i - 1 {
return Err(DslError::MissingComma {
location: ExprSpan::new(tokens[i - 1].span.1, token.span.0),
});
}
}
}
}
},
_ => {},
}
last_token_kind = token.kind;
}
Ok(tokens)
}
fn is_expression_token(kind: TokenKind) -> bool {
matches!(
kind,
TokenKind::Identifier
| TokenKind::StringLiteral
| TokenKind::IntLiteral
| TokenKind::FloatLiteral
| TokenKind::HexLiteral
| TokenKind::RightBrace
| TokenKind::RightBracket
| TokenKind::RightParen
)
}
fn is_opening_delimiter(kind: TokenKind) -> bool {
matches!(
kind,
TokenKind::LeftBracket | TokenKind::LeftParen | TokenKind::LeftBrace
)
}