1pub mod element_type;
3
4use crate::{D2TokenType, language::D2Language, lexer::D2Lexer};
5use oak_core::{
6 GreenNode, Parser,
7 parser::{ParseCache, ParseOutput, ParserState, parse_with_lexer},
8 source::{Source, TextEdit},
9};
10
11pub struct D2Parser<'config> {
13 config: &'config D2Language,
14}
15
16impl<'config> D2Parser<'config> {
17 pub fn new(config: &'config D2Language) -> Self {
19 Self { config }
20 }
21}
22
23impl<'config> Parser<D2Language> for D2Parser<'config> {
24 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<D2Language>) -> ParseOutput<'a, D2Language> {
25 let lexer = D2Lexer::new(self.config);
26 parse_with_lexer(&lexer, text, edits, cache, |state| {
27 while state.not_at_end() {
28 if let Some(_) = self.parse_element(state) {
29 }
31 else {
32 state.advance();
34 }
35 }
36
37 Ok(state.sink.finish_node(0, element_type::D2ElementType::Root))
39 })
40 }
41}
42
43impl<'config> D2Parser<'config> {
44 fn parse_element<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, D2Language, S>) -> Option<&'a GreenNode<'a, D2Language>> {
45 if let Some(shape) = self.parse_shape(state) {
47 return Some(shape);
48 }
49
50 if let Some(connection) = self.parse_connection(state) {
52 return Some(connection);
53 }
54
55 None
56 }
57
58 fn parse_shape<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, D2Language, S>) -> Option<&'a GreenNode<'a, D2Language>> {
59 if !state.at(D2TokenType::Id) {
61 return None;
62 }
63
64 let checkpoint = state.checkpoint();
65
66 state.bump();
68
69 if state.at(D2TokenType::Colon) {
71 state.bump();
72
73 if state.at(D2TokenType::Label) {
75 state.bump();
76 }
77 }
78
79 Some(state.finish_at(checkpoint, element_type::D2ElementType::Shape))
80 }
81
82 fn parse_connection<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, D2Language, S>) -> Option<&'a GreenNode<'a, D2Language>> {
83 if !state.at(D2TokenType::Id) {
85 return None;
86 }
87
88 let checkpoint = state.checkpoint();
89
90 state.bump();
92
93 if !state.at(D2TokenType::Arrow) {
95 return None;
96 }
97
98 state.bump();
100
101 if !state.at(D2TokenType::Id) {
103 return None;
104 }
105
106 state.bump();
108
109 Some(state.finish_at(checkpoint, element_type::D2ElementType::Connection))
110 }
111}