#[cfg(test)]
mod test;
mod lexer {
#![allow(clippy::upper_case_acronyms)]
#[derive(Parser, Debug)]
#[grammar = "parsing/lexer.pest"]
pub struct TokenLexer;
}
use self::lexer::*;
use crate::span_wrap::SpanWrap;
use pest::iterators::Pair;
use pest::Parser;
use std::ops::Range;
use strum_macros::IntoStaticStr;
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct ExtractedToken<'a> {
pub token: Token,
pub slice: &'a str,
pub span: Range<usize>,
}
#[derive(
Serialize, Deserialize, Enum, IntoStaticStr, Debug, Copy, Clone, PartialEq, Eq,
)]
#[serde(rename_all = "kebab-case")]
pub enum Token {
LeftBracket,
LeftBracketAnchor,
LeftBracketSpecial,
RightBracket,
LeftBlock,
LeftBlockEnd,
LeftBlockSpecial,
RightBlock,
DoubleDash,
TripleDash,
LeftDoubleAngle,
ClearFloatNeutral,
ClearFloatCenter,
ClearFloatLeft,
ClearFloatRight,
Pipe,
Equals,
Underscore,
Quote,
Heading,
LineBreak,
ParagraphBreak,
Whitespace,
Strong,
Emphasis,
Underline,
Superscript,
Subscript,
LeftMonospace,
RightMonospace,
Color,
Raw,
LeftRaw,
RightRaw,
BulletItem,
NumberedItem,
LeftLink,
LeftLinkSpecial,
RightLink,
TableColumn,
TableColumnTitle,
RightAlignOpen,
RightAlignClose,
LeftAlignOpen,
LeftAlignClose,
CenterAlignOpen,
CenterAlignClose,
JustifyAlignOpen,
JustifyAlignClose,
Identifier,
Email,
Url,
Variable,
String,
LeftComment,
RightComment,
InputStart,
InputEnd,
Other,
}
impl Token {
pub(crate) fn extract_all<'a>(
log: &slog::Logger,
text: &'a str,
) -> Vec<ExtractedToken<'a>> {
debug!(log, "Running lexer on input");
match TokenLexer::parse(Rule::document, text) {
Ok(pairs) => {
info!(log, "Lexer produced pairs for processing");
let start = ExtractedToken {
token: Token::InputStart,
slice: "",
span: 0..0,
};
let mut tokens = vec![start];
tokens.extend(pairs.map(|pair| Token::convert_pair(log, pair)));
tokens
}
Err(error) => {
error!(log, "Error while lexing input in pest: {}", error);
vec![ExtractedToken {
token: Token::Other,
slice: text,
span: 0..text.len(),
}]
}
}
}
fn convert_pair<'a>(log: &slog::Logger, pair: Pair<'a, Rule>) -> ExtractedToken<'a> {
let rule = pair.as_rule();
let slice = pair.as_str();
let start = pair.as_span().start();
let end = pair.as_span().end();
let span = start..end;
let token = Token::get_from_rule(rule);
debug!(
log,
"Converting pair '{:?}' into token", rule;
"token" => token.name(),
"slice" => pair.as_str(),
"span" => SpanWrap::from(&span),
);
ExtractedToken { token, slice, span }
}
fn get_from_rule(rule: Rule) -> Token {
match rule {
Rule::left_comment => Token::LeftComment,
Rule::right_comment => Token::RightComment,
Rule::left_bracket => Token::LeftBracket,
Rule::left_bracket_anchor => Token::LeftBracketAnchor,
Rule::left_bracket_special => Token::LeftBracketSpecial,
Rule::right_bracket => Token::RightBracket,
Rule::left_block => Token::LeftBlock,
Rule::left_block_end => Token::LeftBlockEnd,
Rule::left_block_special => Token::LeftBlockSpecial,
Rule::right_block => Token::RightBlock,
Rule::color => Token::Color,
Rule::double_dash => Token::DoubleDash,
Rule::triple_dash => Token::TripleDash,
Rule::left_double_angle => Token::LeftDoubleAngle,
Rule::clear_float => Token::ClearFloatNeutral,
Rule::clear_float_center => Token::ClearFloatCenter,
Rule::clear_float_left => Token::ClearFloatLeft,
Rule::clear_float_right => Token::ClearFloatRight,
Rule::pipe => Token::Pipe,
Rule::underscore => Token::Underscore,
Rule::equals => Token::Equals,
Rule::quote => Token::Quote,
Rule::heading => Token::Heading,
Rule::line_break => Token::LineBreak,
Rule::paragraph_break => Token::ParagraphBreak,
Rule::space => Token::Whitespace,
Rule::strong => Token::Strong,
Rule::emphasis => Token::Emphasis,
Rule::underline => Token::Underline,
Rule::superscript => Token::Superscript,
Rule::subscript => Token::Subscript,
Rule::left_monospace => Token::LeftMonospace,
Rule::right_monospace => Token::RightMonospace,
Rule::raw => Token::Raw,
Rule::left_raw => Token::LeftRaw,
Rule::right_raw => Token::RightRaw,
Rule::bullet_item => Token::BulletItem,
Rule::numbered_item => Token::NumberedItem,
Rule::left_link => Token::LeftLink,
Rule::left_link_special => Token::LeftLinkSpecial,
Rule::right_link => Token::RightLink,
Rule::table_column => Token::TableColumn,
Rule::table_column_title => Token::TableColumnTitle,
Rule::open_right_align => Token::RightAlignOpen,
Rule::open_left_align => Token::LeftAlignOpen,
Rule::open_center_align => Token::CenterAlignOpen,
Rule::open_justify_align => Token::JustifyAlignOpen,
Rule::close_right_align => Token::RightAlignClose,
Rule::close_left_align => Token::LeftAlignClose,
Rule::close_center_align => Token::CenterAlignClose,
Rule::close_justify_align => Token::JustifyAlignClose,
Rule::identifier => Token::Identifier,
Rule::email => Token::Email,
Rule::url => Token::Url,
Rule::variable => Token::Variable,
Rule::string => Token::String,
Rule::other => Token::Other,
Rule::EOI => Token::InputEnd,
Rule::char | Rule::document | Rule::token => {
panic!("Received invalid pest rule: {:?}", rule)
}
}
}
#[inline]
pub fn name(self) -> &'static str {
self.into()
}
}
impl slog::Value for Token {
fn serialize(
&self,
_: &slog::Record,
key: slog::Key,
serializer: &mut dyn slog::Serializer,
) -> slog::Result {
serializer.emit_str(key, self.name())
}
}