1pub mod element_type;
2
3use crate::{
4 language::TypstLanguage,
5 lexer::{TypstLexer, token_type::TypstTokenType},
6 parser::element_type::TypstElementType,
7};
8use oak_core::{
9 GreenNode, OakError,
10 parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
11 source::{Source, TextEdit},
12};
13
14pub(crate) type State<'a, S> = ParserState<'a, TypstLanguage, S>;
15
16pub struct TypstParser<'config> {
17 pub(crate) config: &'config TypstLanguage,
18}
19
20impl<'config> TypstParser<'config> {
21 pub fn new(config: &'config TypstLanguage) -> Self {
22 Self { config }
23 }
24
25 fn parse_item<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
26 let kind = state.peek_kind();
27 match kind {
28 Some(TypstTokenType::Heading) => {
29 let checkpoint = state.checkpoint();
30 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Newline) {
32 self.parse_item(state)?;
33 }
34 state.finish_at(checkpoint, TypstElementType::Heading);
35 }
36 Some(TypstTokenType::Hash) => {
37 let checkpoint = state.checkpoint();
38 state.bump(); while state.not_at_end()
41 && state
42 .peek_kind()
43 .map(|k| {
44 matches!(
45 k,
46 TypstTokenType::Identifier
47 | TypstTokenType::Let
48 | TypstTokenType::If
49 | TypstTokenType::Else
50 | TypstTokenType::For
51 | TypstTokenType::While
52 | TypstTokenType::Set
53 | TypstTokenType::Show
54 | TypstTokenType::Import
55 | TypstTokenType::Include
56 )
57 })
58 .unwrap_or(false)
59 {
60 state.bump()
61 }
62
63 if state.peek_kind() == Some(TypstTokenType::LeftBracket) {
64 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::RightBracket) {
66 self.parse_item(state)?;
67 }
68 if state.peek_kind() == Some(TypstTokenType::RightBracket) {
69 state.bump();
70 }
71 }
72 else {
73 while state.not_at_end() && !matches!(state.peek_kind(), Some(TypstTokenType::Newline) | Some(TypstTokenType::Whitespace)) {
75 state.bump()
76 }
77 }
78 state.finish_at(checkpoint, TypstElementType::Quote);
79 }
80 Some(TypstTokenType::Dollar) => {
81 let checkpoint = state.checkpoint();
82 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Dollar) {
84 self.parse_item(state)?;
85 }
86 if state.peek_kind() == Some(TypstTokenType::Dollar) {
87 state.bump()
88 }
89 state.finish_at(checkpoint, TypstElementType::Math);
90 }
91 Some(TypstTokenType::Strong) => {
92 let checkpoint = state.checkpoint();
93 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Strong) {
95 self.parse_item(state)?;
96 }
97 if state.peek_kind() == Some(TypstTokenType::Strong) {
98 state.bump()
99 }
100 state.finish_at(checkpoint, TypstElementType::Strong);
101 }
102 Some(TypstTokenType::Emphasis) => {
103 let checkpoint = state.checkpoint();
104 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Emphasis) {
106 self.parse_item(state)?;
107 }
108 if state.peek_kind() == Some(TypstTokenType::Emphasis) {
109 state.bump()
110 }
111 state.finish_at(checkpoint, TypstElementType::Emphasis);
112 }
113 Some(TypstTokenType::ListItem) => {
114 let checkpoint = state.checkpoint();
115 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Newline) {
117 self.parse_item(state)?;
118 }
119 state.finish_at(checkpoint, TypstElementType::ListItem);
120 }
121 Some(TypstTokenType::EnumItem) => {
122 let checkpoint = state.checkpoint();
123 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Newline) {
125 self.parse_item(state)?;
126 }
127 state.finish_at(checkpoint, TypstElementType::EnumItem);
128 }
129 Some(TypstTokenType::Backtick) => {
130 let checkpoint = state.checkpoint();
131 state.bump(); while state.not_at_end() && state.peek_kind() != Some(TypstTokenType::Backtick) {
133 state.bump()
134 }
135 if state.peek_kind() == Some(TypstTokenType::Backtick) {
136 state.bump()
137 }
138 state.finish_at(checkpoint, TypstElementType::Raw);
139 }
140 _ => {
141 state.bump();
142 }
143 };
144 Ok(())
145 }
146}
147
148impl<'config> Parser<TypstLanguage> for TypstParser<'config> {
149 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<TypstLanguage>) -> ParseOutput<'a, TypstLanguage> {
150 let lexer = TypstLexer::new(&self.config);
151 parse_with_lexer(&lexer, text, edits, cache, |state| {
152 let checkpoint = state.checkpoint();
153
154 while state.not_at_end() {
155 self.parse_item(state)?
156 }
157
158 Ok(state.finish_at(checkpoint, TypstElementType::Root))
159 })
160 }
161}