dbml_language_server/analysis/
parser.rs

1// src/analysis/parser.rs
2use super::token::{Token};
3use crate::ast::*;
4use chumsky::prelude::*;
5
6type ParseError = Simple<Token>;
7
8#[derive(Debug, Clone)]
9#[allow(dead_code)]
10pub enum Error {
11    LexError(Simple<char>),
12    ParseError(Simple<Token>),
13}
14
15pub fn parser() -> impl Parser<Token, Document, Error = ParseError> + Clone {
16    let ident = select! {
17        Token::Identifier(s) => s,
18    }
19    .map_with_span(|name, span| Ident { name, span });
20
21    let string_lit = select! {
22        Token::StringLiteral(s) => s,
23    }
24    .map_with_span(|value, span| StringLiteral { value, span });
25
26    // Relationship type parser (used in both inline refs and standalone refs)
27    let rel_type = choice((
28        just(Token::Minus).to(RelationshipType::OneToOne),
29        just(Token::Lt).to(RelationshipType::OneToMany),
30        just(Token::Gt).to(RelationshipType::ManyToOne),
31        just(Token::LtGt).to(RelationshipType::ManyToMany),
32    ));
33
34    // Referential action parser (for delete/update actions)
35    let ref_action = choice((
36        just(Token::Cascade).to(ReferentialAction::Cascade),
37        just(Token::Restrict).to(ReferentialAction::Restrict),
38        just(Token::NoAction).to(ReferentialAction::NoAction),
39        just(Token::SetNull).to(ReferentialAction::SetNull),
40        just(Token::SetDefault).to(ReferentialAction::SetDefault),
41    ));
42
43    // Inline reference parser (for column settings)
44    let inline_ref = just(Token::Ref)
45        .ignore_then(just(Token::Colon))
46        .ignore_then(rel_type.clone())
47        .then(ident.clone())
48        .then_ignore(just(Token::Dot))
49        .then(ident.clone())
50        .map_with_span(|((relationship, target_table), target_column), span| {
51            InlineRef {
52                target_table,
53                target_column,
54                relationship,
55                span,
56            }
57        });
58
59    // Column settings
60    let column_setting = choice((
61        just(Token::Pk).to(ColumnSetting::PrimaryKey),
62        just(Token::NotNull).to(ColumnSetting::NotNull),
63        just(Token::Null).to(ColumnSetting::Null),
64        just(Token::Unique).to(ColumnSetting::Unique),
65        just(Token::Increment).to(ColumnSetting::Increment),
66        just(Token::Default)
67            .ignore_then(just(Token::Colon))
68            .ignore_then(select! {
69                Token::StringLiteral(s) => DefaultValue::String(s),
70                Token::NumericLiteral(n) => DefaultValue::Number(n),
71                Token::BoolLiteral(b) => DefaultValue::Boolean(b),
72            })
73            .map(ColumnSetting::Default),
74        just(Token::Note)
75            .ignore_then(just(Token::Colon))
76            .ignore_then(string_lit.clone())
77            .map(ColumnSetting::Note),
78        inline_ref.clone().map(ColumnSetting::Ref),
79    ));
80
81    // Column definition
82    let column_type = select! { Token::Identifier(s) => s }
83        .then(
84            // Optional type parameter like varchar(50)
85            just(Token::LParen)
86                .ignore_then(select! { Token::NumericLiteral(n) => n })
87                .then_ignore(just(Token::RParen))
88                .or_not()
89        )
90        .map(|(base_type, param)| match param {
91            Some(p) => format!("{}({})", base_type, p),
92            None => base_type,
93        })
94        .map_with_span(|type_str, span| (type_str, span));
95    
96    let column = ident
97        .clone()
98        .then(column_type)
99        .then(
100            column_setting
101                .separated_by(just(Token::Comma))
102                .allow_trailing()
103                .delimited_by(just(Token::LBracket), just(Token::RBracket))
104                .or_not()
105                .map(|opt| opt.unwrap_or_default()),
106        )
107        .map_with_span(|((name, (col_type, col_type_span)), settings), span| Column {
108            name,
109            col_type,
110            col_type_span,
111            settings,
112            span,
113        });
114
115    // Index definition
116    let index_column = ident.clone().map(IndexColumn::Simple);
117
118    let index_setting = choice((
119        just(Token::Pk).to(IndexSetting::PrimaryKey),
120        just(Token::Unique).to(IndexSetting::Unique),
121    ));
122
123    let index = just(Token::LParen)
124        .ignore_then(index_column.separated_by(just(Token::Comma)))
125        .then_ignore(just(Token::RParen))
126        .then(
127            index_setting
128                .separated_by(just(Token::Comma))
129                .allow_trailing()
130                .delimited_by(just(Token::LBracket), just(Token::RBracket))
131                .or_not()
132                .map(|opt| opt.unwrap_or_default()),
133        )
134        .map_with_span(|(columns, settings), span| Index {
135            columns,
136            settings,
137            span,
138        });
139
140    let indexes_block = just(Token::Indexes)
141        .ignore_then(
142            index
143                .repeated()
144                .at_least(1)
145                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
146        )
147        .map_with_span(|indexes, span| IndexesBlock { indexes, span });
148
149    // Table items
150    let table_item = column
151        .map(TableItem::Column)
152        .or(indexes_block.map(TableItem::Indexes))
153        .or(just(Token::Note)
154            .ignore_then(just(Token::Colon))
155            .ignore_then(string_lit.clone())
156            .map(TableItem::Note));
157
158    // Table definition
159    let table = just(Token::Table)
160        .then(ident.clone())
161        .then(just(Token::As).ignore_then(ident.clone()).or_not())
162        .then(
163            table_item
164                .repeated()
165                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
166        )
167        .map_with_span(|(((_table_token, name), alias), items), span| Table {
168            name,
169            schema: None,
170            alias,
171            items,
172            settings: vec![],
173            span,
174        });
175
176    // Reference definition
177    // Reference settings parser: [delete: action, update: action]
178    let delete_setting = just(Token::Delete)
179        .ignore_then(just(Token::Colon))
180        .ignore_then(ref_action.clone())
181        .map(|action| ("delete", action));
182    
183    let update_setting = just(Token::Update)
184        .ignore_then(just(Token::Colon))
185        .ignore_then(ref_action.clone())
186        .map(|action| ("update", action));
187
188    let ref_settings_block = delete_setting
189        .or(update_setting)
190        .separated_by(just(Token::Comma))
191        .at_least(1)
192        .delimited_by(just(Token::LBracket), just(Token::RBracket))
193        .map(|settings: Vec<(&str, ReferentialAction)>| {
194            let mut on_delete = None;
195            let mut on_update = None;
196            for (key, action) in settings {
197                match key {
198                    "delete" => on_delete = Some(action),
199                    "update" => on_update = Some(action),
200                    _ => {}
201                }
202            }
203            (on_delete, on_update)
204        });
205
206    let ref_def = just(Token::Ref)
207        .ignore_then(ident.clone().or_not())
208        .then_ignore(just(Token::Colon))
209        .then(ident.clone())
210        .then_ignore(just(Token::Dot))
211        .then(ident.clone())
212        .then(rel_type.clone())
213        .then(ident.clone())
214        .then_ignore(just(Token::Dot))
215        .then(ident.clone())
216        .then(ref_settings_block.or_not())
217        .map_with_span(
218            |((((((name, from_table), from_col), relationship), to_table), to_col), settings), span| {
219                let (on_delete, on_update) = settings.unwrap_or((None, None));
220                Ref {
221                    name,
222                    from_table,
223                    from_columns: vec![from_col],
224                    to_table,
225                    to_columns: vec![to_col],
226                    relationship,
227                    on_delete,
228                    on_update,
229                    span,
230                }
231            },
232        );
233
234    // Enum definition
235    let enum_member = ident
236        .clone()
237        .then(
238            just(Token::LBracket)
239                .ignore_then(just(Token::Note))
240                .ignore_then(just(Token::Colon))
241                .ignore_then(string_lit.clone())
242                .then_ignore(just(Token::RBracket))
243                .or_not(),
244        )
245        .map_with_span(|(name, note), span| EnumMember { name, note, span });
246
247    let enum_def = just(Token::Enum)
248        .ignore_then(ident.clone())
249        .then(
250            enum_member
251                .repeated()
252                .at_least(1)
253                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
254        )
255        .map_with_span(|(name, members), span| Enum {
256            name,
257            members,
258            span,
259        });
260
261    // Project definition
262    let project_setting = just(Token::Note)
263        .ignore_then(just(Token::Colon))
264        .ignore_then(string_lit.clone())
265        .map(|note| crate::ast::ProjectSetting::Note(note))
266        .or(ident.clone()
267            .then_ignore(just(Token::Colon))
268            .then(string_lit.clone())
269            .map(|(key, value)| {
270                if key.name.to_lowercase() == "database_type" {
271                    crate::ast::ProjectSetting::DatabaseType(value.value)
272                } else {
273                    // For other settings, store as database_type for now
274                    // In a full implementation, you'd have a generic setting type
275                    crate::ast::ProjectSetting::DatabaseType(format!("{}: {}", key.name, value.value))
276                }
277            })
278        );
279
280    let project = just(Token::Project)
281        .ignore_then(ident.clone().or_not())
282        .then(
283            project_setting
284                .repeated()
285                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
286        )
287        .map_with_span(|(name, settings), span| Project {
288            name,
289            settings,
290            span,
291        });
292
293    // Document
294    let document_item = table
295        .map(DocumentItem::Table)
296        .or(ref_def.map(DocumentItem::Ref))
297        .or(enum_def.map(DocumentItem::Enum))
298        .or(project.map(DocumentItem::Project));
299
300    document_item
301        .repeated()
302        .then_ignore(end())
303        .map_with_span(|items, span| Document { items, span })
304}
305
306pub fn parse(src: &str) -> Result<Document, Vec<Error>> {
307    use super::lexer::lexer;
308    use chumsky::Stream;
309
310    let tokens = lexer().parse(src).map_err(|errs| {
311        errs.into_iter().map(Error::LexError).collect::<Vec<_>>()
312    })?;
313    
314    // Create a stream of (Token, Span) tuples
315    let token_spans: Vec<(Token, Span)> = tokens
316        .iter()
317        .map(|st| (st.token.clone(), st.span.clone()))
318        .collect();
319    
320    let len = src.len();
321    let stream = Stream::from_iter(len..len+1, token_spans.into_iter());
322
323    parser().parse(stream).map_err(|errs| {
324        errs.into_iter().map(Error::ParseError).collect::<Vec<_>>()
325    })
326}
327
328#[cfg(test)]
329mod tests {
330    use super::*;
331
332    #[test]
333    fn test_parse_table() {
334        let input = r#"
335            Table users {
336                id int [pk]
337                name varchar
338            }
339        "#;
340        let result = parse(input);
341        assert!(result.is_ok());
342    }
343
344    #[test]
345    fn test_parse_enum() {
346        let input = r#"
347            Enum status {
348                active
349                inactive
350            }
351        "#;
352        let result = parse(input);
353        assert!(result.is_ok());
354    }
355}