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    // Inline reference parser (for column settings)
35    let inline_ref = just(Token::Ref)
36        .ignore_then(just(Token::Colon))
37        .ignore_then(rel_type.clone())
38        .then(ident.clone())
39        .then_ignore(just(Token::Dot))
40        .then(ident.clone())
41        .map_with_span(|((relationship, target_table), target_column), span| {
42            InlineRef {
43                target_table,
44                target_column,
45                relationship,
46                span,
47            }
48        });
49
50    // Column settings
51    let column_setting = choice((
52        just(Token::Pk).to(ColumnSetting::PrimaryKey),
53        just(Token::NotNull).to(ColumnSetting::NotNull),
54        just(Token::Null).to(ColumnSetting::Null),
55        just(Token::Unique).to(ColumnSetting::Unique),
56        just(Token::Increment).to(ColumnSetting::Increment),
57        just(Token::Default)
58            .ignore_then(just(Token::Colon))
59            .ignore_then(select! {
60                Token::StringLiteral(s) => DefaultValue::String(s),
61                Token::NumericLiteral(n) => DefaultValue::Number(n),
62                Token::BoolLiteral(b) => DefaultValue::Boolean(b),
63            })
64            .map(ColumnSetting::Default),
65        just(Token::Note)
66            .ignore_then(just(Token::Colon))
67            .ignore_then(string_lit.clone())
68            .map(ColumnSetting::Note),
69        inline_ref.clone().map(ColumnSetting::Ref),
70    ));
71
72    // Column definition
73    let column_type = select! { Token::Identifier(s) => s }
74        .then(
75            // Optional type parameter like varchar(50)
76            just(Token::LParen)
77                .ignore_then(select! { Token::NumericLiteral(n) => n })
78                .then_ignore(just(Token::RParen))
79                .or_not()
80        )
81        .map(|(base_type, param)| match param {
82            Some(p) => format!("{}({})", base_type, p),
83            None => base_type,
84        });
85    
86    let column = ident
87        .clone()
88        .then(column_type)
89        .then(
90            column_setting
91                .separated_by(just(Token::Comma))
92                .allow_trailing()
93                .delimited_by(just(Token::LBracket), just(Token::RBracket))
94                .or_not()
95                .map(|opt| opt.unwrap_or_default()),
96        )
97        .map_with_span(|((name, col_type), settings), span| Column {
98            name,
99            col_type,
100            settings,
101            span,
102        });
103
104    // Index definition
105    let index_column = ident.clone().map(IndexColumn::Simple);
106
107    let index_setting = choice((
108        just(Token::Pk).to(IndexSetting::PrimaryKey),
109        just(Token::Unique).to(IndexSetting::Unique),
110    ));
111
112    let index = just(Token::LParen)
113        .ignore_then(index_column.separated_by(just(Token::Comma)))
114        .then_ignore(just(Token::RParen))
115        .then(
116            index_setting
117                .separated_by(just(Token::Comma))
118                .allow_trailing()
119                .delimited_by(just(Token::LBracket), just(Token::RBracket))
120                .or_not()
121                .map(|opt| opt.unwrap_or_default()),
122        )
123        .map_with_span(|(columns, settings), span| Index {
124            columns,
125            settings,
126            span,
127        });
128
129    let indexes_block = just(Token::Indexes)
130        .ignore_then(
131            index
132                .repeated()
133                .at_least(1)
134                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
135        )
136        .map_with_span(|indexes, span| IndexesBlock { indexes, span });
137
138    // Table items
139    let table_item = column
140        .map(TableItem::Column)
141        .or(indexes_block.map(TableItem::Indexes))
142        .or(just(Token::Note)
143            .ignore_then(just(Token::Colon))
144            .ignore_then(string_lit.clone())
145            .map(TableItem::Note));
146
147    // Table definition
148    let table = just(Token::Table)
149        .then(ident.clone())
150        .then(just(Token::As).ignore_then(ident.clone()).or_not())
151        .then(
152            table_item
153                .repeated()
154                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
155        )
156        .map_with_span(|(((_table_token, name), alias), items), span| Table {
157            name,
158            schema: None,
159            alias,
160            items,
161            settings: vec![],
162            span,
163        });
164
165    // Reference definition
166    let ref_def = just(Token::Ref)
167        .ignore_then(ident.clone().or_not())
168        .then_ignore(just(Token::Colon))
169        .then(ident.clone())
170        .then_ignore(just(Token::Dot))
171        .then(ident.clone())
172        .then(rel_type.clone())
173        .then(ident.clone())
174        .then_ignore(just(Token::Dot))
175        .then(ident.clone())
176        .map_with_span(
177            |(((((name, from_table), from_col), relationship), to_table), to_col), span| Ref {
178                name,
179                from_table,
180                from_columns: vec![from_col],
181                to_table,
182                to_columns: vec![to_col],
183                relationship,
184                span,
185            },
186        );
187
188    // Enum definition
189    let enum_member = ident
190        .clone()
191        .then(
192            just(Token::LBracket)
193                .ignore_then(just(Token::Note))
194                .ignore_then(just(Token::Colon))
195                .ignore_then(string_lit.clone())
196                .then_ignore(just(Token::RBracket))
197                .or_not(),
198        )
199        .map_with_span(|(name, note), span| EnumMember { name, note, span });
200
201    let enum_def = just(Token::Enum)
202        .ignore_then(ident.clone())
203        .then(
204            enum_member
205                .repeated()
206                .at_least(1)
207                .delimited_by(just(Token::LBrace), just(Token::RBrace)),
208        )
209        .map_with_span(|(name, members), span| Enum {
210            name,
211            members,
212            span,
213        });
214
215    // Project definition
216    let project = just(Token::Project)
217        .ignore_then(ident.clone().or_not())
218        .then(
219            just(Token::LBrace)
220                .ignore_then(just(Token::RBrace))
221                .to(vec![]),
222        )
223        .map_with_span(|(name, settings), span| Project {
224            name,
225            settings,
226            span,
227        });
228
229    // Document
230    let document_item = table
231        .map(DocumentItem::Table)
232        .or(ref_def.map(DocumentItem::Ref))
233        .or(enum_def.map(DocumentItem::Enum))
234        .or(project.map(DocumentItem::Project));
235
236    document_item
237        .repeated()
238        .then_ignore(end())
239        .map_with_span(|items, span| Document { items, span })
240}
241
242pub fn parse(src: &str) -> Result<Document, Vec<Error>> {
243    use super::lexer::lexer;
244    use chumsky::Stream;
245
246    let tokens = lexer().parse(src).map_err(|errs| {
247        errs.into_iter().map(Error::LexError).collect::<Vec<_>>()
248    })?;
249    
250    // Create a stream of (Token, Span) tuples
251    let token_spans: Vec<(Token, Span)> = tokens
252        .iter()
253        .map(|st| (st.token.clone(), st.span.clone()))
254        .collect();
255    
256    let len = src.len();
257    let stream = Stream::from_iter(len..len+1, token_spans.into_iter());
258
259    parser().parse(stream).map_err(|errs| {
260        errs.into_iter().map(Error::ParseError).collect::<Vec<_>>()
261    })
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267
268    #[test]
269    fn test_parse_table() {
270        let input = r#"
271            Table users {
272                id int [pk]
273                name varchar
274            }
275        "#;
276        let result = parse(input);
277        assert!(result.is_ok());
278    }
279
280    #[test]
281    fn test_parse_enum() {
282        let input = r#"
283            Enum status {
284                active
285                inactive
286            }
287        "#;
288        let result = parse(input);
289        assert!(result.is_ok());
290    }
291}