use crate::lexer::token::Token;
use crate::common::{ OperatorType, KeywordType, MaybeIgnore };
use crate::tracking::SourceRegion;
use super::iterator::{ ParserIterator, UnwrapIndexWithIterator };
use super::ast::{ Pattern, PatternData, StructPatternField, StructPatternFieldData, NamedExpressionPattern };
use super::expr::expression;
use super::pred;
fn patt_ignore (it: &mut ParserIterator) -> Option<Pattern> {
Some(Pattern {
data: PatternData::Ignore,
source: it.expect_ignore()?
})
}
fn patt_ident_or_chain (it: &mut ParserIterator) -> Option<Pattern> {
let (ident, SourceRegion { start, end }) = it.expect_ident()?;
Some(if let Some(_) = it.expect_specific_op(OperatorType::Dot) {
let mut ident_chain = vec! [ ident ];
let mut last_end = end;
let mut end = None;
while it.valid() {
if end.is_some() {
if let Some(_) = it.expect_specific_op(OperatorType::Dot) {
last_end = end.unwrap();
end = None;
} else {
break
}
} else {
if let Some((ident, SourceRegion { start: _, end: e })) = it.expect_ident() {
ident_chain.push(ident);
end = Some(e);
} else {
it.simple_error("Expected identifier to follow . in identifier chain of pattern".to_string());
end = Some(last_end);
break
}
}
}
Pattern {
data: PatternData::IdentifierChain(ident_chain),
source: SourceRegion {
start,
end: end.unwrap_index_with_iterator(it, "Expected identifier to follow . in identifier chain of pattern")
}
}
} else {
Pattern {
data: PatternData::Identifier(ident),
source: SourceRegion { start, end }
}
})
}
fn tuple_field (it: &mut ParserIterator) -> Option<NamedExpressionPattern> {
if it.curr_opt()?.is_ident() {
if let Some(next) = it.peek_next() {
if next.is_specific_op(OperatorType::Assign) {
let (name, source) = it.expect_ident()?;
it.advance();
return if let Some(expression ) = expression(it) {
Some(NamedExpressionPattern::NamedExpression { name, expression })
} else {
it.simple_error("Expected value expression to follow = in tuple pattern field".to_string());
it.synchronize(pred::contextual(
pred::specific_op(OperatorType::LeftParen),
pred::specific_op(OperatorType::RightParen),
pred::specific_op(OperatorType::Comma)
));
Some(NamedExpressionPattern::Pattern(Pattern {
data: PatternData::Identifier(name),
source
}))
};
}
}
}
return Some(NamedExpressionPattern::Pattern(pattern(it)?));
}
fn patt_tuple (it: &mut ParserIterator) -> Option<Pattern> {
let SourceRegion { start, end: _ } = it.expect_specific_op(OperatorType::LeftParen)?;
let mut fields = Vec::new();
let mut sep = true;
let mut end = None;
while it.valid() {
let err_msg;
if let Some(SourceRegion { start: _, end: e }) = it.expect_specific_op(OperatorType::RightParen) {
end = Some(e);
break
} else if it.expect_specific_op(OperatorType::Comma).is_some() {
sep = true;
continue
} else if sep {
if let Some(field) = tuple_field(it) {
fields.push(field);
sep = false;
continue
} else {
err_msg = "field pattern for";
}
} else {
err_msg = ", to separate fields or ) to end";
}
it.simple_error(format!("Expected {} tuple pattern", err_msg));
let (op, SourceRegion { start: _, end: e }) = it.synchronize_on_ops(pred::contextual(
pred::specific_op(OperatorType::LeftParen),
pred::specific_op(OperatorType::RightParen),
pred::specific_op(OperatorType::Comma)
))?;
if op == OperatorType::Comma {
sep = true;
} else {
end = Some(e);
break
}
}
Some(Pattern {
data: PatternData::Tuple(fields),
source: SourceRegion {
start,
end: end.unwrap_index_with_iterator(it, "Expected ) to end tuple pattern")
}
})
}
fn patt_expr (it: &mut ParserIterator) -> Option<Pattern> {
let SourceRegion { start, end: _ } = it.expect_specific_op(OperatorType::Assign)?;
if let Some(expr) = expression(it) {
let source = SourceRegion { start, end: expr.source.end };
Some(Pattern {
data: PatternData::Expression(expr),
source
})
} else {
it.simple_error("Expected expression to follow = in pattern".to_string());
None
}
}
fn patt_named_tuple (left: Pattern, it: &mut ParserIterator) -> Option<Pattern> {
let right = patt_tuple(it)?;
let source = SourceRegion { start: left.source.start, end: right.source.end };
Some(Pattern {
data: PatternData::NamedTuple {
identifier_chain: left.data.extract_ident_or_chain(),
fields: right.data.extract_tuple_fields()
},
source
})
}
fn struct_field (it: &mut ParserIterator) -> Option<StructPatternField> {
let (name, name_source) = it.expect_ident()?;
let mut data;
if it.expect_specific_op(OperatorType::Colon).is_some() {
if let Some(patt) = pattern(it) {
if patt.data.is_ignore() {
it.warning(SourceRegion { start: name_source.start, end: patt.source.end }, format!("To specify ignored struct field use '{} as _' not '{}: _'", name, name));
data = StructPatternFieldData::Terminal { alias: Some(MaybeIgnore::Ignore), value: None };
} else {
data = StructPatternFieldData::Subpattern(patt);
}
} else {
it.simple_error("Expected subpattern to follow : in struct pattern field".to_string());
return None;
};
} else {
data = StructPatternFieldData::empty();
if it.expect_specific_op(OperatorType::Cast).is_some() {
if let Some((alias, _)) = it.expect_ident_or_ignore() {
data.set_alias(alias);
} else {
it.simple_error("Expected alias identifier to follow as in struct pattern field".to_string());
it.synchronize(pred::contextual(
pred::specific_op(OperatorType::LeftBrace),
pred::specific_op(OperatorType::RightBrace),
pred::or(
pred::specific_op(OperatorType::Assign),
pred::specific_op(OperatorType::Comma)
)
));
}
}
if it.expect_specific_op(OperatorType::Assign).is_some() {
if let Some(expr) = expression(it) {
data.set_value(expr);
} else {
it.simple_error("Expected value expression to follow = in struct pattern field".to_string());
}
}
}
Some(StructPatternField { name, data })
}
fn patt_struct (left: Pattern, it: &mut ParserIterator) -> Option<Pattern> {
it.expect_specific_op(OperatorType::LeftBrace)?;
let mut fields = Vec::new();
let mut sep = true;
let mut end = None;
while it.valid() {
let err_msg;
if let Some(SourceRegion { start: _, end: e }) = it.expect_specific_op(OperatorType::RightBrace) {
end = Some(e);
break
} else if it.expect_specific_op(OperatorType::Comma).is_some() {
sep = true;
continue
} else if sep {
if let Some(field) = struct_field(it) {
fields.push(field);
sep = false;
continue
} else {
err_msg = "field pattern for";
}
} else {
err_msg = ", to separate fields or ] to end";
}
it.simple_error(format!("Expected {} struct pattern", err_msg));
let (op, SourceRegion { start: _, end: e }) = it.synchronize_on_ops(pred::contextual(
pred::specific_op(OperatorType::LeftBrace),
pred::specific_op(OperatorType::RightBrace),
pred::specific_op(OperatorType::Comma)
))?;
if op == OperatorType::Comma {
sep = true;
} else {
end = Some(e);
break
}
}
let source = SourceRegion {
start: left.source.start,
end: end.unwrap_index_with_iterator(it, "Expected ] to end struct pattern")
};
Some(Pattern {
data: PatternData::Struct{
identifier_chain: left.data.extract_ident_or_chain(),
fields
},
source
})
}
fn patt_else (left: Pattern, it: &mut ParserIterator) -> Option<Pattern> {
it.expect_specific_kw(KeywordType::Else)?;
if let Some(right) = pattern(it) {
let source = SourceRegion { start: left.source.start, end: right.source.end };
Some(Pattern {
data: PatternData::Else { left: Box::new(left), right: Box::new(right) },
source
})
} else {
it.simple_error("Expected right operand pattern for else operator".to_string());
None
}
}
fn patt_enums (left: Pattern, it: &mut ParserIterator) -> Option<Pattern> {
it.expect_specific_op(OperatorType::LeftBracket)?;
if let Some(expression) = expression(it) {
let source = SourceRegion {
start: left.source.start,
end: if let Some(SourceRegion { start: _, end }) = it.expect_specific_op(OperatorType::RightBracket) {
end
} else {
it.simple_error("Expected } to close multi-enum pattern expression (e.g. path.to.EnumTy { expr })".to_string());
let (_, SourceRegion { start: _, end }) = it.synchronize_on_ops(pred::pair_counter(
pred::specific_op(OperatorType::LeftBracket),
pred::specific_op(OperatorType::RightBracket)
))?;
end
}
};
Some(Pattern {
data: PatternData::Enums { identifier_chain: left.data.extract_ident_or_chain(), expression },
source
})
} else {
it.simple_error("Expected expression for multi-enum pattern (e.g. path.to.EnumTy { expr })".to_string());
it.synchronize_on_ops(pred::pair_counter(
pred::specific_op(OperatorType::LeftBracket),
pred::specific_op(OperatorType::RightBracket)
));
None
}
}
struct PrefixParselet {
predicate: fn (&Token) -> bool,
procedure: fn (&mut ParserIterator) -> Option<Pattern>,
}
struct InfixParselet {
predicate: fn (&Pattern, &Token) -> bool,
procedure: fn (Pattern, &mut ParserIterator) -> Option<Pattern>,
}
macro_rules! pfx {
( $( ( $pred: expr, $proc: expr ) ), * $(,)? ) => {
&[ $( PrefixParselet { predicate: $pred, procedure: $proc } ), * ]
};
}
macro_rules! ifx {
( $( ( $pred: expr, $proc: expr ) ), * $(,)? ) => {
&[ $( InfixParselet { predicate: $pred, procedure: $proc } ), * ]
};
}
const PREFIX_PARSELETS: &[PrefixParselet] = pfx! [
(|t| t.is_ignore(), patt_ignore),
(|t| t.is_ident(), patt_ident_or_chain),
(|t| t.is_specific_op(OperatorType::LeftParen), patt_tuple),
(|t| t.is_specific_op(OperatorType::Assign), patt_expr)
];
const INFIX_PARSELETS: &[InfixParselet] = ifx! [
(|p, t| p.data.is_ident_or_chain() && t.is_specific_op(OperatorType::LeftParen), patt_named_tuple),
(|p, t| p.data.is_ident_or_chain() && t.is_specific_op(OperatorType::LeftBrace), patt_struct),
(|p, t| p.data.is_ident_or_chain() && t.is_specific_op(OperatorType::LeftBracket), patt_enums),
(|_, t| t.is_specific_kw(KeywordType::Else), patt_else),
];
fn get_prefix_parselet (token: &Token) -> Option<&'static PrefixParselet> {
for prefix in PREFIX_PARSELETS {
if (prefix.predicate)(token) {
return Some(prefix);
}
}
None
}
fn get_infix_parselet (left: &Pattern, token: &Token) -> Option<&'static InfixParselet> {
for infix in INFIX_PARSELETS {
if (infix.predicate)(left, token) {
return Some(infix);
}
}
None
}
pub fn pattern (it: &mut ParserIterator) -> Option<Pattern> {
if let Some(prefix_parselet) = get_prefix_parselet(it.curr_opt()?) {
let mut left = (prefix_parselet.procedure)(it)?;
while it.valid() {
if let Some(infix_parselet) = get_infix_parselet(&left, it.curr()) {
left = (infix_parselet.procedure)(left, it)?;
} else {
break
}
}
Some(left)
} else {
it.simple_error("Failed to find semantic match for this token in the context of a pattern".to_string());
None
}
}