Add word-level parsing

This commit is contained in:
Juno Takano 2025-12-18 02:20:11 -03:00
commit 198bc12507
34 changed files with 743 additions and 446 deletions

View file

@ -1,5 +1,7 @@
use std::path::PathBuf;
use crate::prelude::*;
#[derive(Clone, Debug, Default)]
pub struct Arguments {
pub hostname: String,
@ -47,10 +49,7 @@ fn parse(defaults: &Arguments, args: &[String]) -> Arguments {
} else if argument.eq("-g") || argument.eq("--graph") {
out_args.graph_path = PathBuf::from(parameter);
} else {
crate::dev::log(
&parse,
&format!("Dropped unrecognized argument {argument}"),
);
log!("Dropped unrecognized argument {argument}");
}
} else {
panic!("Argument {arg:?} has no corresponding value")

View file

@ -1,10 +1,6 @@
use token::{Token};
use parsers::{line::Line, word::Word};
use lexeme::Lexeme;
use parser::{token::Token, lexeme::Lexeme};
mod token;
pub mod lexeme;
pub mod parsers;
pub mod parser;
pub trait Parseable: Into<Token> {
fn probe(lexeme: &Lexeme) -> bool;
@ -16,22 +12,6 @@ type Probe = fn(&Lexeme) -> bool;
type Lexer = fn(&Lexeme) -> Token;
type LexMap<'lm> = &'lm [(Probe, Lexer)];
fn make_lexmap<DefaultToken: Parseable>(base: LexMap) -> Vec<(Probe, Lexer)> {
let mut vector: Vec<(Probe, Lexer)> = base.to_vec();
fn adapter<D: Parseable>(lex: &Lexeme) -> Token {
D::lex(lex).into()
}
vector.push((DefaultToken::probe, adapter::<DefaultToken>));
vector
}
pub fn parse<DefaultLineToken: Parseable, DefaultWordToken: Parseable>(
text: &str,
) -> String {
let escaped_text = tera::escape_html(text);
parsers::line::parser::read::<DefaultLineToken>(
&parsers::word::parser::read::<DefaultWordToken>(&escaped_text),
)
pub fn parse(text: &str) -> String {
parser::read(text)
}

View file

@ -1,24 +0,0 @@
use super::parsers::{line::Line, word::Word};
#[derive(Clone)]
pub enum Lexeme {
Line(Line),
Word(Word),
}
impl Lexeme {
pub fn to_raw(&self) -> String {
match *self {
Lexeme::Line(ref d) => d.raw.clone(),
Lexeme::Word(ref d) => d.raw.clone(),
}
}
pub fn to_vec(self) -> Vec<String> {
self.to_raw().split(' ').map(str::to_string).collect()
}
pub fn first(self) -> Option<String> {
self.to_vec().first().map(String::to_owned)
}
}

View file

@ -0,0 +1,186 @@
use std::slice::Iter;
use crate::prelude::*;
use super::{Parseable as _, Token, LexMap};
use token::{
anchor::Anchor, linebreak::LineBreak, paragraph::Paragraph, header::Header,
preformat::PreFormat, literal::Literal,
};
use lexeme::{Lexeme, compound::Compound};
pub mod token;
pub mod lexeme;
const LEXMAP: LexMap = &[
(Anchor::probe, |word| Token::Anchor(Anchor::lex(word))),
(LineBreak::probe, |word| {
Token::LineBreak(LineBreak::lex(word))
}),
(Literal::probe, |word| Token::Literal(Literal::lex(word))),
];
enum Context {
None,
Paragraph,
Header(u8),
PreFormat,
}
fn lex(text: &str, map: LexMap) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut state = Context::None;
let splits = split(text);
let mut iter = splits.iter();
while let Some(word) = iter.next() {
let compound = cluster(word, &mut iter);
let lexeme = Lexeme::Compound(compound);
match state {
Context::None => {
if Header::probe(&lexeme) {
let header = Header::lex(&lexeme);
state = Context::Header(header.get_level());
tokens.push(Token::Header(header));
continue;
} else if PreFormat::probe(&lexeme) {
tokens.push(Token::PreFormat(PreFormat::new(true)));
state = Context::PreFormat;
continue;
} else if Paragraph::probe(&lexeme) {
tokens.push(Token::Paragraph(Paragraph::new(true)));
state = Context::Paragraph;
}
},
Context::Paragraph => {
if word == "\n" {
tokens.push(Token::Paragraph(Paragraph::new(false)));
state = Context::None;
}
},
Context::Header(n) => {
if word == "\n" {
tokens.push(Token::Header(Header::from_u8(n, false)));
state = Context::None;
}
},
Context::PreFormat => {
if PreFormat::probe(&lexeme) {
tokens.push(Token::PreFormat(PreFormat::new(false)));
state = Context::None;
continue;
}
},
}
for &(ref probe, lex) in map {
if probe(&lexeme) {
tokens.push(lex(&lexeme));
break;
}
}
}
tokens
}
fn split(text: &str) -> Vec<String> {
text.replace("\n", " \n ")
.split(' ')
.map(str::to_string)
.collect()
}
// this could be eliminated if space were a token
fn join<'i, Iterator>(rendered_tokens: Iterator) -> String
where
Iterator: IntoIterator<Item = &'i str>,
{
fn stick(current: &str, next: &str) -> bool {
// this could be in a dedicated type
fn is_tag(s: &str) -> bool {
s.starts_with("<") && s.ends_with('>')
}
fn is_opening(s: &str) -> bool {
is_tag(s) && !s.contains("</")
}
fn is_closing(s: &str) -> bool {
is_tag(s) && s.contains("</")
}
fn is_inline(s: &str) -> bool {
is_tag(s) && s.starts_with("<a")
}
log!("On {current}[?]{next}");
if is_inline(next) {
log!("Pushing space because {next} is inline");
false
} else if is_closing(next) {
log!("Not pushing space because {next} is closing");
true
} else if is_opening(current) {
log!("Not pushing space because {current} is opening");
true
} else {
false
}
}
let mut iterator = rendered_tokens.into_iter();
let mut out_string = String::new();
if let Some(mut current) = iterator.next() {
out_string.push_str(current);
for next in iterator {
if stick(current, next) {
out_string.push_str(next);
} else {
out_string.push(' ');
out_string.push_str(next);
}
current = next;
}
}
out_string
}
fn parse(tokens: &[Token]) -> String {
let rendered: Vec<String> = tokens.iter().map(Token::render).collect();
join(rendered.iter().map(String::as_str))
}
fn cluster<'c>(word: &str, iter: &mut Iter<'c, String>) -> Compound {
if word.starts_with('|') {
log!("Found opener {word}");
let mut parts = vec![word];
if let Some(first) = parts.first()
&& first.ends_with('|')
{
log!("Returning atomic cluster");
Compound::new(&parts.join(" "))
} else {
log!("Seeking a boundary");
for next_raw in iter {
if next_raw.contains('|') {
log!("Found end of cluster {next_raw:?}");
parts.push(next_raw);
break;
} else {
parts.push(next_raw);
log!("Onto next word from {next_raw}");
}
}
log!("Returning cluster {parts:?}");
Compound::new(&parts.join(" "))
}
} else {
Compound::new(word)
}
}
pub(super) fn read(text: &str) -> String {
parse(&lex(text, LEXMAP))
}

View file

@ -0,0 +1,39 @@
#[derive(Clone)]
pub enum Lexeme {
Compound(compound::Compound),
}
pub mod compound;
impl Lexeme {
pub fn to_raw(&self) -> String {
match *self {
Lexeme::Compound(ref d) => d.raw.clone(),
}
}
/// # Panics
/// Panics if number of chars for a single lexeme exceeds `i2::MAX`
pub fn count_char(&self, c: char) -> i32 {
let count = self.to_raw().chars().filter(|&n| n == c).count();
match i32::try_from(count) {
Ok(i) => i,
Err(e) => {
panic!("Wild char number {count} is a bit much: {e:#?}");
},
}
}
pub fn split_chars(&self) -> Vec<char> {
let vector: Vec<char> = self.to_raw().chars().collect();
vector
}
pub fn split_words(self) -> Vec<String> {
self.to_raw().split(' ').map(str::to_string).collect()
}
pub fn first(self) -> Option<String> {
self.split_words().first().map(String::to_owned)
}
}

View file

@ -0,0 +1,12 @@
#[derive(Clone)]
pub struct Compound {
pub raw: String,
}
impl Compound {
pub fn new(text: &str) -> Compound {
Compound {
raw: text.to_owned(),
}
}
}

View file

@ -0,0 +1,75 @@
use crate::syntax::content::Parseable as _;
pub mod literal;
pub mod anchor;
pub mod linebreak;
pub mod paragraph;
pub mod span;
pub mod header;
pub mod preformat;
pub enum Token {
Anchor(anchor::Anchor),
Header(header::Header),
LineBreak(linebreak::LineBreak),
Literal(literal::Literal),
Paragraph(paragraph::Paragraph),
PreFormat(preformat::PreFormat),
Span(span::Span),
}
impl Token {
pub fn render(&self) -> String {
match *self {
Token::Anchor(ref d) => d.render(),
Token::Header(ref d) => d.render(),
Token::LineBreak(ref d) => d.render(),
Token::Literal(ref d) => d.render(),
Token::Paragraph(ref d) => d.render(),
Token::PreFormat(ref d) => d.render(),
Token::Span(ref d) => d.render(),
}
}
}
impl From<paragraph::Paragraph> for Token {
fn from(d: paragraph::Paragraph) -> Token {
Token::Paragraph(d)
}
}
impl From<header::Header> for Token {
fn from(d: header::Header) -> Token {
Token::Header(d)
}
}
impl From<span::Span> for Token {
fn from(d: span::Span) -> Token {
Token::Span(d)
}
}
impl From<literal::Literal> for Token {
fn from(d: literal::Literal) -> Token {
Token::Literal(d)
}
}
impl From<anchor::Anchor> for Token {
fn from(d: anchor::Anchor) -> Token {
Token::Anchor(d)
}
}
impl From<linebreak::LineBreak> for Token {
fn from(d: linebreak::LineBreak) -> Token {
Token::LineBreak(d)
}
}
impl From<preformat::PreFormat> for Token {
fn from(d: preformat::PreFormat) -> Token {
Token::PreFormat(d)
}
}

View file

@ -0,0 +1,68 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, parser::lexeme::Lexeme};
pub struct Anchor {
text: String,
destination: String,
}
impl Parseable for Anchor {
fn probe(lexeme: &Lexeme) -> bool {
let pipe_count = lexeme.count_char('|');
let chars = lexeme.split_chars();
let c1 = *match chars.first() {
Some(c) => c,
None => return false,
};
let cn = *match chars.last() {
Some(c) => c,
None => return false,
};
if !(1_i32..=3_i32).contains(&pipe_count) {
return false;
}
if lexeme.to_raw().matches("||").count() > 0 {
return false;
}
if pipe_count == 1 {
c1 != '|' && cn != '|'
} else if pipe_count == 2 {
c1 == '|' && cn != '|'
} else if pipe_count == 3 {
c1 == '|' && cn == '|'
} else {
false
}
}
fn lex(lexeme: &Lexeme) -> Anchor {
let parts: Vec<String> = lexeme
.to_raw()
.split('|')
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect();
assert!(parts.len() == 2, "Parts should always be 2: {parts:?}");
let text = parts.first().unwrap_or_else(|| unreachable!());
let destination = parts.get(1).unwrap_or_else(|| unreachable!());
Anchor {
text: text.to_owned(),
destination: destination.to_owned(),
}
}
fn render(&self) -> String {
format!(r#"<a href="{}">{}</a>"#, &self.destination, &self.text)
}
}
impl Display for Anchor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Anchor: <{}> to <{}>", &self.text, &self.destination)
}
}

View file

@ -0,0 +1,137 @@
use crate::{
prelude::*,
syntax::content::{Parseable, Lexeme},
};
use std::fmt::Display;
pub struct Header {
open: Option<bool>,
level: Level,
}
impl Header {
pub fn new(level: Level, open: bool) -> Header {
Header {
level,
open: Some(open),
}
}
pub fn from_u8(level: u8, open: bool) -> Header {
Header {
level: Level::from_u8(level),
open: Some(open),
}
}
pub fn get_level(&self) -> u8 {
match self.level {
Level::One => 1,
Level::Two => 2,
Level::Three => 3,
Level::Four => 4,
Level::Five => 5,
Level::Six => 6,
}
}
}
impl Parseable for Header {
fn probe(lexeme: &Lexeme) -> bool {
if lexeme
.split_chars()
.into_iter()
.filter(|e| *e != '#')
.count()
== 0
{
let level = lexeme.to_raw().len();
lexeme.clone().split_words().len() == 1 && level > 0 && level <= 6
} else {
false
}
}
fn lex(lexeme: &Lexeme) -> Header {
Header::new(lexeme.to_raw().len().into(), true)
}
fn render(&self) -> String {
if let Some(open) = self.open {
if open {
format!("<h{}>", &self.level)
} else {
format!("</h{}>", &self.level)
}
} else {
panic!("Attempt to render a header tag while open state is unknown")
}
}
}
impl Display for Header {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(open) = self.open {
if open {
write!(f, "Level {} Open Header", self.level)
} else {
write!(f, "Level {} Closed Header", self.level)
}
} else {
write!(f, "Level {} Header (Unknown open state)", self.level)
}
}
}
pub enum Level {
One,
Two,
Three,
Four,
Five,
Six,
}
impl Level {
fn from_u8(u: u8) -> Level {
if u <= 1 {
Level::One
} else if u == 2 {
Level::Two
} else if u == 3 {
Level::Three
} else if u == 4 {
Level::Four
} else if u == 5 {
Level::Five
} else {
Level::Six
}
}
}
impl From<usize> for Level {
fn from(z: usize) -> Level {
let u8 = match u8::try_from(z) {
Ok(u) => u,
Err(e) => {
log!("Truncating header level {z} to 6: {e:?}");
6_u8
},
};
Level::from_u8(u8)
}
}
impl Display for Level {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Level::One => write!(f, "1"),
Level::Two => write!(f, "2"),
Level::Three => write!(f, "3"),
Level::Four => write!(f, "4"),
Level::Five => write!(f, "5"),
Level::Six => write!(f, "6"),
}
}
}

View file

@ -0,0 +1,26 @@
use std::fmt::Display;
use crate::{
syntax::content::{Parseable, parser::lexeme::Lexeme},
};
pub struct LineBreak {}
impl Parseable for LineBreak {
fn probe(lexeme: &Lexeme) -> bool {
lexeme.to_raw() == "\n"
}
fn lex(_lexeme: &Lexeme) -> LineBreak {
LineBreak {}
}
fn render(&self) -> String {
"\n".to_owned()
}
}
impl Display for LineBreak {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Line Break")
}
}

View file

@ -1,18 +1,18 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, lexeme::Lexeme};
use crate::syntax::content::{Parseable, parser::lexeme::Lexeme};
pub struct Literal {
text: String,
}
impl Parseable for Literal {
fn probe(lexeme: &Lexeme) -> bool {
!lexeme.to_raw().is_empty()
fn probe(_lexeme: &Lexeme) -> bool {
true
}
fn lex(lexeme: &Lexeme) -> Literal {
Literal {
text: lexeme.to_raw().trim().to_owned(),
text: lexeme.to_raw(),
}
}

View file

@ -0,0 +1,53 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, parser::lexeme::Lexeme};
pub struct Paragraph {
open: Option<bool>,
}
impl Paragraph {
pub fn new(open: bool) -> Paragraph {
Paragraph { open: Some(open) }
}
}
impl Parseable for Paragraph {
fn probe(lexeme: &Lexeme) -> bool {
// lexeme for paragraph is any non-whitespace, parser knows the context
let raw = lexeme.to_raw();
let trimmed = raw.trim();
!trimmed.is_empty() && trimmed != "\n"
}
fn lex(_lexeme: &Lexeme) -> Paragraph {
Paragraph { open: None }
}
fn render(&self) -> String {
if let Some(open) = self.open {
if open {
"<p>".to_owned()
} else {
"</p>".to_owned()
}
} else {
panic!(
"Attempt to render a paragraph tag while open state is unknown"
)
}
}
}
impl Display for Paragraph {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(open) = self.open {
if open {
write!(f, "Open Paragraph")
} else {
write!(f, "Closed Paragraph")
}
} else {
write!(f, "Unitialized Paragraph (Unknown open state)")
}
}
}

View file

@ -0,0 +1,43 @@
use crate::{
syntax::content::{Parseable, Lexeme},
};
pub struct PreFormat {
open: Option<bool>,
}
impl PreFormat {
pub fn new(open: bool) -> PreFormat {
PreFormat { open: Some(open) }
}
}
impl Parseable for PreFormat {
fn probe(lexeme: &Lexeme) -> bool {
let chars = lexeme.split_chars();
if let Some(first_char) = chars.first() {
*first_char == '`'
} else {
false
}
}
fn lex(_lexeme: &Lexeme) -> PreFormat {
PreFormat { open: None }
}
fn render(&self) -> String {
if let Some(o) = self.open {
if o {
"<pre>".to_owned()
} else {
"</pre>".to_owned()
}
} else {
panic!(
"Attempt to render a preformat tag while open state is unknown"
)
}
}
}

View file

@ -0,0 +1,49 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, parser::lexeme::Lexeme};
pub struct Span {
open: Option<bool>,
}
impl Span {
pub fn new(open: bool) -> Span {
Span { open: Some(open) }
}
}
impl Parseable for Span {
fn probe(_lexeme: &Lexeme) -> bool {
// there is no lexeme for span
false
}
fn lex(_lexeme: &Lexeme) -> Span {
Span { open: None }
}
fn render(&self) -> String {
if let Some(open) = self.open {
if open {
"<span>".to_owned()
} else {
"</span>".to_owned()
}
} else {
panic!("Attempt to render a span tag while open state is unknown")
}
}
}
impl Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(open) = self.open {
if open {
write!(f, "Open Span")
} else {
write!(f, "Closed Span")
}
} else {
write!(f, "Span (Unknown open state)")
}
}
}

View file

@ -1,2 +0,0 @@
pub mod line;
pub mod word;

View file

@ -1,33 +0,0 @@
use crate::syntax::content::lexeme::Lexeme;
pub mod parser;
pub mod elements;
#[derive(Clone)]
pub struct Line {
pub raw: String,
pub first: String,
}
impl Line {
pub fn new(text: &str) -> Line {
let vec: Vec<&str> = text.split(" ").collect();
Line {
raw: text.to_owned(),
first: vec.first().unwrap_or_else(|| unreachable!()).to_string(),
}
}
}
impl From<Lexeme> for Line {
fn from(lexeme: Lexeme) -> Line {
match lexeme {
Lexeme::Word(w) => Line {
raw: w.raw.clone(),
first: w.raw.split(' ').next().unwrap_or_default().to_owned(),
},
Lexeme::Line(l) => l,
}
}
}

View file

@ -1,3 +0,0 @@
pub mod header;
pub mod paragraph;
pub mod span;

View file

@ -1,80 +0,0 @@
use crate::{
dev::log,
syntax::content::{Parseable, Lexeme},
};
use std::fmt::Display;
enum Level {
One,
Two,
Three,
Four,
Five,
Six,
}
impl Display for Level {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Level::One => write!(f, "1"),
Level::Two => write!(f, "2"),
Level::Three => write!(f, "3"),
Level::Four => write!(f, "4"),
Level::Five => write!(f, "5"),
Level::Six => write!(f, "6"),
}
}
}
pub struct Header {
level: Level,
text: String,
}
impl Header {
fn new(level: usize, text: &str) -> Header {
Header {
level: match level {
1 => Level::One,
2 => Level::Two,
3 => Level::Three,
4 => Level::Four,
5 => Level::Five,
6 => Level::Six,
_ => {
panic!("Attempted to construct a header with invalid level")
},
},
text: text.to_owned(),
}
}
}
impl Parseable for Header {
fn probe(lexeme: &Lexeme) -> bool {
let first = lexeme.clone().first().unwrap_or_default();
!first.trim().is_empty()
&& first.replace("#", "").is_empty()
&& first.len() <= 6
}
fn lex(lexeme: &Lexeme) -> Header {
let first = lexeme.clone().first().unwrap_or_else(|| unreachable!());
let header_level = &first.len();
log(&Header::lex, &format!("Header level is {header_level}"));
let header_text = lexeme.to_raw().replace(&first, "");
Header::new(*header_level, &header_text)
}
fn render(&self) -> String {
format!("<h{}>{}</h{0}>", &self.level, self.text)
}
}
impl Display for Header {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Level {} Header: <{}>", &self.level, self.text)
}
}

View file

@ -1,28 +0,0 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, lexeme::Lexeme};
pub struct Paragraph {
text: String,
}
impl Parseable for Paragraph {
fn probe(lexeme: &Lexeme) -> bool {
!lexeme.to_raw().trim().is_empty()
}
fn lex(lexeme: &Lexeme) -> Paragraph {
Paragraph {
text: lexeme.to_raw().trim().to_owned(),
}
}
fn render(&self) -> String {
format!("<p>{}</p>", &self.text)
}
}
impl Display for Paragraph {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Paragraph: <{}>", &self.text)
}
}

View file

@ -1,28 +0,0 @@
use std::fmt::Display;
use crate::syntax::content::{Parseable, lexeme::Lexeme};
pub struct Span {
text: String,
}
impl Parseable for Span {
fn probe(lexeme: &Lexeme) -> bool {
!lexeme.to_raw().trim().is_empty()
}
fn lex(lexeme: &Lexeme) -> Span {
Span {
text: lexeme.to_raw().trim().to_owned(),
}
}
fn render(&self) -> String {
format!("<span>{}</span>", &self.text)
}
}
impl Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Span: <{}>", &self.text)
}
}

View file

@ -1,38 +0,0 @@
use crate::syntax::content::{
LexMap, Line, Parseable, Token, parsers::line::elements::header::Header,
make_lexmap, Lexeme,
};
const LEXMAP: LexMap =
&[(Header::probe, |line| Token::Header(Header::lex(line)))];
pub(in crate::syntax::content) fn read<DefaultToken: Parseable>(
text: &str,
) -> String {
parse(&lex(text, &make_lexmap::<DefaultToken>(LEXMAP)))
}
fn lex(text: &str, map: LexMap) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
for raw_line in text.lines() {
let line = Lexeme::Line(Line::new(raw_line));
for &(ref probe, lex) in map {
if probe(&line) {
tokens.push(lex(&line));
break;
}
}
}
tokens
}
fn parse(tokens: &[Token]) -> String {
tokens
.iter()
.map(Token::render)
.collect::<Vec<_>>()
.join("\n")
}

View file

@ -1,15 +0,0 @@
pub mod parser;
pub mod elements;
#[derive(Clone)]
pub struct Word {
pub raw: String,
}
impl Word {
pub fn new(text: &str) -> Word {
Word {
raw: text.to_owned(),
}
}
}

View file

@ -1,2 +0,0 @@
pub mod literal;
pub mod anchor;

View file

@ -1,33 +0,0 @@
// use std::fmt::Display;
// use crate::syntax::content::{Parseable, Line};
//
// pub struct Anchor {
// text: String,
// destination: String,
// }
//
// impl Parseable for Anchor {
// fn probe(line: &Line) -> bool {
// let candidate = line.raw.split(' ');
// !line.first.trim().is_empty()
// && line.first.replace("#", "").is_empty()
// && line.first.len() <= 6
// }
//
// fn lex(line: &Line) -> Self {
// Self {
// text: line.raw.trim().to_owned(),
// destination: t
// }
// }
//
// fn render(&self) -> String {
// format!(r#"<a href="{}">{}</a>"#, &self.destination, &self.text)
// }
// }
//
// impl Display for Anchor {
// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// write!(f, "Anchor: <{}>", &self.text)
// }
// }

View file

@ -1,37 +0,0 @@
use crate::syntax::content::parsers::word::elements::literal::Literal;
use crate::syntax::content::{Parseable, Token, Word, LexMap, make_lexmap};
use crate::syntax::content::lexeme::Lexeme;
const LEXMAP: LexMap =
&[(Literal::probe, |line| Token::Literal(Literal::lex(line)))];
pub(in crate::syntax::content) fn read<DefaultToken: Parseable>(
text: &str,
) -> String {
parse(&lex(text, &make_lexmap::<DefaultToken>(LEXMAP)))
}
fn lex(text: &str, map: LexMap) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
for raw_word in text.split(" ") {
let word = Lexeme::Word(Word::new(raw_word));
for &(ref probe, lex) in map {
if probe(&word) {
tokens.push(lex(&word));
break;
}
}
}
tokens
}
fn parse(tokens: &[Token]) -> String {
tokens
.iter()
.map(Token::render)
.collect::<Vec<_>>()
.join(" ")
}

View file

@ -1,47 +0,0 @@
use super::Parseable as _;
use super::parsers::word::elements::{literal::Literal};
use super::parsers::line::elements::{
paragraph::Paragraph, header::Header, span::Span,
};
pub enum Token {
Paragraph(Paragraph),
Header(Header),
Span(Span),
Literal(Literal),
}
impl Token {
pub fn render(&self) -> String {
match *self {
Token::Paragraph(ref d) => d.render(),
Token::Header(ref d) => d.render(),
Token::Span(ref d) => d.render(),
Token::Literal(ref d) => d.render(),
}
}
}
impl From<Paragraph> for Token {
fn from(d: Paragraph) -> Token {
Token::Paragraph(d)
}
}
impl From<Header> for Token {
fn from(d: Header) -> Token {
Token::Header(d)
}
}
impl From<Span> for Token {
fn from(d: Span) -> Token {
Token::Span(d)
}
}
impl From<Literal> for Token {
fn from(d: Literal) -> Token {
Token::Literal(d)
}
}