emo_lan/
lib.rs

1use wasm_bindgen::prelude::*;
2use regex::Regex;
3use lazy_static::lazy_static;
4
5lazy_static! {
6    static ref DOCUMENT_START: Regex = Regex::new(r"πŸ“„").unwrap();
7    static ref TEXT_PATTERN: Regex = Regex::new(r"πŸ”€([^πŸ”€]*)πŸ”€").unwrap();
8    static ref IMAGE_PATTERN: Regex = Regex::new(r"πŸ–ΌοΈ\[(.*?)\]\((.*?)\)").unwrap();
9}
10
11#[wasm_bindgen]
12pub fn compile_to_html(input: &str) -> String {
13    println!("Raw input: {:?}", input);
14    let tokens = lex(input);
15    println!("Tokens: {:?}", tokens);
16    let ast = parse(tokens);
17    println!("AST: {:?}", ast);
18    match analyze(&ast) {
19        Ok(_) => match compile(&ast) {
20            Ok(html) => {
21                println!("Generated HTML: {}", html);
22                html
23            }
24            Err(_) => "Compile error".to_string(),
25        },
26        Err(_) => "Semantic analysis error".to_string(),
27    }
28}
29
30pub fn lex(input: &str) -> Vec<Token> {
31    println!("Raw input to lex: {:?}", input);
32    let input = input.trim();
33    let mut tokens = Vec::new();
34
35    if DOCUMENT_START.is_match(input) {
36        tokens.push(Token::DocumentStart);
37    }
38
39    for cap in TEXT_PATTERN.captures_iter(input) {
40        if let Some(text) = cap.get(1) {
41            tokens.push(Token::Text(text.as_str().to_string()));
42        }
43    }
44
45    for cap in IMAGE_PATTERN.captures_iter(input) {
46        if let (Some(alt), Some(url)) = (cap.get(1), cap.get(2)) {
47            tokens.push(Token::Image {
48                url: url.as_str().to_string(),
49                alt: alt.as_str().to_string(),
50            });
51        }
52    }
53
54    if tokens.is_empty() {
55        tokens.push(Token::Unknown);
56    }
57
58    println!("Tokens: {:?}", tokens);
59    tokens
60}
61
62pub fn parse(tokens: Vec<Token>) -> ASTNode {
63    let mut nodes = Vec::new();
64
65    for token in tokens {
66        match token {
67            Token::DocumentStart => nodes.push(ASTNode::DocumentStart),
68            Token::Text(text) => nodes.push(ASTNode::Paragraph(text)),
69            Token::Image { url, alt } => nodes.push(ASTNode::Image { url, alt }),
70            Token::Unknown => nodes.push(ASTNode::Unknown),
71        }
72    }
73
74    ASTNode::Document(nodes)
75}
76
77pub fn analyze(ast: &ASTNode) -> Result<(), SemanticError> {
78    match ast {
79        ASTNode::Document(nodes) => {
80            if nodes.is_empty() || !matches!(nodes[0], ASTNode::DocumentStart) {
81                return Err(SemanticError::MissingDocumentStart);
82            }
83        }
84        _ => {}
85    }
86
87    Ok(())
88}
89
90pub fn compile(ast: &ASTNode) -> Result<String, CompileError> {
91    match ast {
92        ASTNode::Document(nodes) => {
93            let mut html = String::from("<!DOCTYPE html>\n<html>\n<body>\n");
94            for node in nodes {
95                html.push_str(&compile_node(node)?);
96            }
97            html.push_str("</body>\n</html>");
98            Ok(html)
99        }
100        _ => Err(CompileError::GeneralError),
101    }
102}
103
104fn compile_node(node: &ASTNode) -> Result<String, CompileError> {
105    match node {
106        ASTNode::DocumentStart => Ok(String::new()),
107        ASTNode::Paragraph(text) => Ok(format!("<p>{}</p>\n", text)),
108        ASTNode::Image { url, alt } => Ok(format!("<img src=\"{}\" alt=\"{}\" />\n", url, alt)),
109        _ => Err(CompileError::GeneralError),
110    }
111}
112
113#[derive(Debug, PartialEq)]
114pub enum Token {
115    DocumentStart,
116    Text(String),
117    Image { url: String, alt: String },
118    Unknown,
119}
120
121#[derive(Debug)]
122pub enum ASTNode {
123    DocumentStart,
124    Paragraph(String),
125    Image { url: String, alt: String },
126    Document(Vec<ASTNode>),
127    Unknown,
128}
129
130#[derive(Debug)]
131pub enum SemanticError {
132    MissingDocumentStart,
133}
134
135#[derive(Debug)]
136pub enum CompileError {
137    GeneralError,
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_compile_to_html() {
146        let input = "πŸ“„πŸ”€Hello WorldπŸ”€πŸ–ΌοΈ[ァンプル画像](https://example.com/image.jpg)";
147        let output = compile_to_html(input);
148        assert_eq!(
149            output,
150            r#"<!DOCTYPE html>
151<html>
152<body>
153<p>Hello World</p>
154<img src="https://example.com/image.jpg" alt="ァンプル画像" />
155</body>
156</html>"#
157        );
158    }
159}