Skip to main content

oak_html/parser/
mod.rs

1use crate::{kind::HtmlSyntaxKind, language::HtmlLanguage, lexer::HtmlLexer};
2use oak_core::{
3    GreenNode, OakError,
4    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
5    source::{Source, TextEdit},
6};
7
8pub(crate) type State<'a, S> = ParserState<'a, HtmlLanguage, S>;
9
10pub struct HtmlParser {
11    pub(crate) _config: HtmlLanguage,
12}
13
14impl HtmlParser {
15    pub fn new(config: HtmlLanguage) -> Self {
16        Self { _config: config }
17    }
18
19    pub(crate) fn parse_root_internal<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<&'a GreenNode<'a, HtmlLanguage>, OakError> {
20        let checkpoint = state.checkpoint();
21
22        while state.not_at_end() {
23            match state.peek_kind() {
24                Some(HtmlSyntaxKind::TagOpen) => self.parse_tag(state)?,
25                Some(HtmlSyntaxKind::Doctype) => {
26                    state.bump();
27                }
28                Some(HtmlSyntaxKind::Comment) => {
29                    state.bump();
30                }
31                _ => {
32                    state.bump();
33                }
34            }
35        }
36
37        Ok(state.finish_at(checkpoint, HtmlSyntaxKind::Document.into()))
38    }
39
40    fn parse_tag<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
41        use crate::kind::HtmlSyntaxKind::*;
42        let cp = state.checkpoint();
43        state.expect(TagOpen).ok();
44        state.expect(TagName).ok();
45
46        while state.not_at_end() && !matches!(state.peek_kind(), Some(TagClose) | Some(TagSelfClose)) {
47            if state.at(AttributeName) {
48                let _attr_cp = state.checkpoint();
49                state.bump(); // AttributeName
50                if state.eat(Equal) {
51                    state.eat(Quote);
52                    state.eat(AttributeValue);
53                    state.eat(Quote);
54                }
55                // 这里没有具体的 Attribute 节点类型,暂不 finish
56            }
57            else {
58                state.advance();
59            }
60        }
61
62        if state.eat(TagSelfClose) {
63            // 自闭合标签
64        }
65        else if state.eat(TagClose) {
66            // 这里应该递归解析子节点,直到遇到对应的结束标签
67            // 简化处理:跳过直到结束标签
68            while state.not_at_end() && !state.at(TagSlashOpen) {
69                if state.at(TagOpen) {
70                    self.parse_tag(state)?;
71                }
72                else {
73                    state.advance();
74                }
75            }
76            if state.eat(TagSlashOpen) {
77                state.eat(TagName);
78                state.expect(TagClose).ok();
79            }
80        }
81
82        state.finish_at(cp, HtmlSyntaxKind::Element.into());
83        Ok(())
84    }
85}
86
87impl Parser<HtmlLanguage> for HtmlParser {
88    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<HtmlLanguage>) -> ParseOutput<'a, HtmlLanguage> {
89        let lexer = HtmlLexer::new(&self._config);
90        parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_root_internal(state))
91    }
92}