use super::{Scanner, Result, basic::*};
use crate::utils::char::{CharPredicate, Stream};
use crate::lexeme::{RId, ROp, Lexeme, QName, ModuleId};
use crate::lexeme::Lexeme::{ReservedId, ReservedOp, Identifier, Operator, QIdentifier, QOperator};
impl<I: std::io::Read> Scanner<I> {
pub fn id_or_sym(&mut self) -> Result<Lexeme> {
alt!(self, Self::q_var_id_or_q_sym,
Self::q_con_id,
Self::con_id_,
Self::con_sym_or_reserved_op,
Self::var_sym_or_reserved_op,
Self::var_id_or_reserved_id);
Result::RetryLater(())
}
fn con_id_(&mut self) -> Option<Lexeme> {
self.con_id().map(Identifier)
}
fn con_id(&mut self) -> Option<String> {
analyse!(self, c: Large, name: {c.to_string()}{String::push} *any!(Small, Large, Digit, '\''));
Some(name)
}
fn var_id_or_reserved_id(&mut self) -> Option<Lexeme> {
analyse!(self, c: Small, name: {c.to_string()}{String::push} *any!(Small, Large, Digit, '\''));
Some(match name.as_str() {
"case" => ReservedId(RId::Case),
"class" => ReservedId(RId::Class),
"data" => ReservedId(RId::Data),
"default" => ReservedId(RId::Default),
"deriving" => ReservedId(RId::Deriving),
"do" => ReservedId(RId::Do),
"else" => ReservedId(RId::Else),
"foreign" => ReservedId(RId::Foreign),
"if" => ReservedId(RId::If),
"import" => ReservedId(RId::Import),
"in" => ReservedId(RId::In),
"infix" => ReservedId(RId::Infix),
"infixl" => ReservedId(RId::Infixl),
"infixr" => ReservedId(RId::Infixr),
"instance" => ReservedId(RId::Instance),
"let" => ReservedId(RId::Let),
"module" => ReservedId(RId::Module),
"newtype" => ReservedId(RId::Newtype),
"of" => ReservedId(RId::Of),
"then" => ReservedId(RId::Then),
"type" => ReservedId(RId::Type),
"where" => ReservedId(RId::Where),
"_" => ReservedId(RId::Wildcard),
_ => Identifier(name),
})
}
fn mod_id(&mut self) -> Option<ModuleId> {
let names: Option<Vec<String>> = self.sep_by(
Self::con_id, choice!('.'), Vec::new(), Vec::push);
names.map(ModuleId)
}
fn var_sym_or_reserved_op(&mut self) -> Option<Lexeme> {
analyse!(self, c: all!(Symbol, not!(':')), name: {c.to_string()}{String::push} *Symbol);
Some(match name.as_str() {
".." => ReservedOp(ROp::DotDot),
"=" => ReservedOp(ROp::EqualSign),
"\\" => ReservedOp(ROp::Backslash),
"|" => ReservedOp(ROp::Pipe),
"<-" => ReservedOp(ROp::LeftArrow),
"->" => ReservedOp(ROp::RightArrow),
"@" => ReservedOp(ROp::AtSign),
"^" => ReservedOp(ROp::Tilde),
"=>" => ReservedOp(ROp::DoubleRightArrow),
_ => Operator(name),
})
}
fn con_sym_or_reserved_op(&mut self) -> Option<Lexeme> {
analyse!(self, ':', name: {':'.to_string()}{String::push} *Symbol);
Some(match name.as_str() {
":" => ReservedOp(ROp::Colon),
"::" => ReservedOp(ROp::ColonColon),
_ => Operator(name),
})
}
fn q_con_id(&mut self) -> Option<Lexeme> {
let init = QName::new(self.con_id()?);
Option::map(
self.some(|scanner| {
analyse!(scanner, '.');
scanner.con_id()
}, init, QName::append),
QIdentifier,
)
}
fn q_var_id_or_q_sym(&mut self) -> Option<Lexeme> {
let module = self.mod_id()?;
analyse!(self, '.');
Some(match simple_alt!(self,
Self::var_id_or_reserved_id,
Self::var_sym_or_reserved_op,
Self::con_sym_or_reserved_op)? {
Identifier(name) => QIdentifier(QName { module, name }),
Operator(name) => QOperator(QName { module, name }),
_ => return None,
})
}
}
#[cfg(test)]
mod tests {
use crate::scanner::test_scanner_on;
use crate::utils::setup_logger;
use crate::utils::Result3::Success;
use crate::lexeme::{Lexeme, QName, ModuleId};
use crate::lexeme::Lexeme::{Identifier, QIdentifier, QOperator};
#[test]
fn test_identifier() {
setup_logger();
fn test(input: &str, res: Lexeme, next: Option<char>) {
trace!(scanner, "test on {:?} ...", input);
test_scanner_on(input, method!(id_or_sym), Success(res), next);
}
test("some'Identifier_42", Identifier("some'Identifier_42".to_string()), None);
test("Ctor_''233'_", Identifier("Ctor_''233'_".to_string()), None);
test("Mod.SubMod.Class", QIdentifier(QName {
module: ModuleId(vec!["Mod".to_string(), "SubMod".to_string()]),
name: "Class".to_string(),
}), None);
test("F..", QOperator(QName {
module: ModuleId(vec!["F".to_string()]),
name: ".".to_string(),
}), None);
test("F.", Identifier("F".to_string()), Some('.'));
}
}