Skip to main content

nounsql_core/
parser.rs

1use crate::ast::*;
2use crate::diag::Diagnostic;
3use crate::lexer::{Tok, Token, lex};
4use crate::span::{Span, Spanned};
5
6pub fn parse(src: &str) -> (Document, Vec<Diagnostic>) {
7    let (tokens, mut diags) = lex(src);
8    let mut parser = Parser {
9        tokens,
10        pos: 0,
11        diags: Vec::new(),
12    };
13    let doc = parser.document();
14    diags.append(&mut parser.diags);
15    (doc, diags)
16}
17
18/// 文の解析に失敗したことを表す。行末まで読み飛ばして回復する。
19struct Bail;
20type PResult<T> = Result<T, Bail>;
21
22const ATTR_KEYS: &[&str] = &["type", "null", "default", "on_update", "comment"];
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24enum BlockKind {
25    Table,
26    Mixin,
27}
28
29const RELATION_KEYS: &[&str] = &["fk", "alias", "via", "comment"];
30const VALUE_FNS: &[&str] = &["noun", "singular", "plural"];
31
32struct Parser {
33    tokens: Vec<Token>,
34    pos: usize,
35    diags: Vec<Diagnostic>,
36}
37
38impl Parser {
39    // ---------- トークン操作 ----------
40
41    fn peek(&self) -> &Tok {
42        &self.tokens[self.pos].value
43    }
44
45    fn peek_at(&self, n: usize) -> &Tok {
46        let i = (self.pos + n).min(self.tokens.len() - 1);
47        &self.tokens[i].value
48    }
49
50    fn span(&self) -> Span {
51        self.tokens[self.pos].span
52    }
53
54    fn prev_span(&self) -> Span {
55        self.tokens[self.pos.saturating_sub(1)].span
56    }
57
58    fn bump(&mut self) -> Token {
59        let tok = self.tokens[self.pos].clone();
60        if self.pos + 1 < self.tokens.len() {
61            self.pos += 1;
62        }
63        tok
64    }
65
66    fn at(&self, tok: &Tok) -> bool {
67        self.peek() == tok
68    }
69
70    fn eat(&mut self, tok: &Tok) -> bool {
71        if self.at(tok) {
72            self.bump();
73            true
74        } else {
75            false
76        }
77    }
78
79    fn at_keyword(&self, kw: &str) -> bool {
80        matches!(self.peek(), Tok::Ident(s) if s == kw)
81    }
82
83    fn error(&mut self, span: Span, msg: impl Into<String>) -> Bail {
84        self.diags.push(Diagnostic::error(span, msg));
85        Bail
86    }
87
88    fn expect(&mut self, tok: Tok) -> PResult<Span> {
89        if self.at(&tok) {
90            Ok(self.bump().span)
91        } else {
92            let span = self.span();
93            let found = self.peek().describe();
94            Err(self.error(span, format!("{} が必要。{} が来た", tok.describe(), found)))
95        }
96    }
97
98    fn expect_ident(&mut self, what: &str) -> PResult<Name> {
99        match self.peek().clone() {
100            Tok::Ident(name) => {
101                let span = self.bump().span;
102                Ok(Spanned::new(name, span))
103            }
104            other => {
105                let span = self.span();
106                Err(self.error(span, format!("{what} が必要。{} が来た", other.describe())))
107            }
108        }
109    }
110
111    fn expect_string(&mut self, what: &str) -> PResult<Spanned<String>> {
112        match self.peek().clone() {
113            Tok::Str(s) => {
114                let span = self.bump().span;
115                Ok(Spanned::new(s, span))
116            }
117            other => {
118                let span = self.span();
119                Err(self.error(span, format!("{what} が必要。{} が来た", other.describe())))
120            }
121        }
122    }
123
124    fn skip_newlines(&mut self) {
125        while self.at(&Tok::Newline) {
126            self.bump();
127        }
128    }
129
130    /// 行末まで読み飛ばす。1行1文なので、これで次の文から再開できる。
131    fn recover_line(&mut self) {
132        while !matches!(self.peek(), Tok::Newline | Tok::Eof | Tok::RBrace) {
133            self.bump();
134        }
135    }
136
137    /// 対応する `}` まで読み飛ばす。
138    fn recover_block(&mut self) {
139        let mut depth = 0usize;
140        loop {
141            match self.peek() {
142                Tok::Eof => return,
143                Tok::LBrace => depth += 1,
144                Tok::RBrace => {
145                    self.bump();
146                    if depth == 0 {
147                        return;
148                    }
149                    depth -= 1;
150                    continue;
151                }
152                _ => {}
153            }
154            self.bump();
155        }
156    }
157
158    /// 文の終わり(改行 / `}` / EOF)を要求する。
159    fn end_of_statement(&mut self) -> PResult<()> {
160        match self.peek() {
161            Tok::Newline | Tok::Eof | Tok::RBrace => Ok(()),
162            other => {
163                let span = self.span();
164                let found = other.describe();
165                Err(self.error(span, format!("1行1文。行末が必要だが {found} が来た")))
166            }
167        }
168    }
169
170    // ---------- トップレベル ----------
171
172    fn document(&mut self) -> Document {
173        let mut doc = Document::default();
174        loop {
175            self.skip_newlines();
176            if self.at(&Tok::Eof) {
177                break;
178            }
179            if self.item(&mut doc).is_err() {
180                self.recover_line();
181            }
182        }
183        doc
184    }
185
186    fn item(&mut self, doc: &mut Document) -> PResult<()> {
187        let Tok::Ident(kw) = self.peek().clone() else {
188            let span = self.span();
189            let found = self.peek().describe();
190            return Err(self.error(span, format!("トップレベルに書けない: {found}")));
191        };
192        match kw.as_str() {
193            "naming" | "constraints" => {
194                let block = self.config_block()?;
195                let slot = if kw == "naming" {
196                    &mut doc.naming
197                } else {
198                    &mut doc.constraints
199                };
200                if let Some(prev) = slot {
201                    let span = block.name.span;
202                    let prev_span = prev.name.span;
203                    self.diags.push(
204                        Diagnostic::error(span, format!("`{kw}` ブロックが重複している"))
205                            .with_label(prev_span, "最初の定義"),
206                    );
207                } else {
208                    *slot = Some(block);
209                }
210                Ok(())
211            }
212            "nouns" => {
213                let block = self.nouns_block()?;
214                if let Some(prev) = &doc.nouns {
215                    let prev_span = prev.span;
216                    self.diags.push(
217                        Diagnostic::error(block.span, "`nouns` ブロックが重複している")
218                            .with_label(prev_span, "最初の定義"),
219                    );
220                } else {
221                    doc.nouns = Some(block);
222                }
223                Ok(())
224            }
225            "mixin" => {
226                let m = self.mixin_block()?;
227                doc.mixins.push(m);
228                Ok(())
229            }
230            "table" => {
231                let t = self.table_block()?;
232                doc.tables.push(t);
233                Ok(())
234            }
235            "blueprint" => {
236                let b = self.blueprint_block()?;
237                doc.blueprints.push(b);
238                Ok(())
239            }
240            "associate" | "apply_blueprint" => {
241                let m = self.macro_call()?;
242                doc.macros.push(m);
243                self.end_of_statement()
244            }
245            other => {
246                let span = self.span();
247                Err(self.error(span, format!("トップレベルに書けないキーワード `{other}`")))
248            }
249        }
250    }
251
252    // ---------- 設定ブロック ----------
253
254    fn config_block(&mut self) -> PResult<ConfigBlock> {
255        let name = self.expect_ident("ブロック名")?;
256        let start = name.span;
257        if self.expect(Tok::LBrace).is_err() {
258            self.recover_block();
259            return Err(Bail);
260        }
261        let mut entries = Vec::new();
262        loop {
263            self.skip_newlines();
264            if self.at(&Tok::RBrace) || self.at(&Tok::Eof) {
265                break;
266            }
267            match self.assign() {
268                Ok(a) => {
269                    entries.push(a);
270                    if self.end_of_statement().is_err() {
271                        self.recover_line();
272                    }
273                }
274                Err(Bail) => self.recover_line(),
275            }
276        }
277        let end = self.expect(Tok::RBrace).unwrap_or(self.prev_span());
278        Ok(ConfigBlock {
279            name,
280            entries,
281            span: start.join(end),
282        })
283    }
284
285    fn assign(&mut self) -> PResult<Assign> {
286        let key = self.expect_ident("設定キー")?;
287        self.expect(Tok::Eq)?;
288        let value = self.value()?;
289        Ok(Assign { key, value })
290    }
291
292    fn nouns_block(&mut self) -> PResult<NounsBlock> {
293        let start = self.expect_ident("`nouns`")?.span;
294        if self.expect(Tok::LBrace).is_err() {
295            self.recover_block();
296            return Err(Bail);
297        }
298        let mut entries = Vec::new();
299        loop {
300            self.skip_newlines();
301            if self.at(&Tok::RBrace) || self.at(&Tok::Eof) {
302                break;
303            }
304            match self.noun_entry() {
305                Ok(e) => {
306                    entries.push(e);
307                    if self.end_of_statement().is_err() {
308                        self.recover_line();
309                    }
310                }
311                Err(Bail) => self.recover_line(),
312            }
313        }
314        let end = self.expect(Tok::RBrace).unwrap_or(self.prev_span());
315        Ok(NounsBlock {
316            entries,
317            span: start.join(end),
318        })
319    }
320
321    fn noun_entry(&mut self) -> PResult<Noun> {
322        let singular = self.expect_ident("単数形")?;
323        let plural = self.expect_ident("複数形")?;
324        let comment = match self.peek() {
325            Tok::Str(_) => Some(self.expect_string("コメント")?),
326            _ => None,
327        };
328        Ok(Noun {
329            singular,
330            plural,
331            comment,
332        })
333    }
334
335    // ---------- mixin / table ----------
336
337    fn mixin_block(&mut self) -> PResult<Mixin> {
338        let start = self.expect_ident("`mixin`")?.span;
339        let name = self.expect_ident("mixin名")?;
340        let (members, end) = self.member_block(BlockKind::Mixin)?;
341        Ok(Mixin {
342            name,
343            members,
344            span: start.join(end),
345        })
346    }
347
348    fn table_block(&mut self) -> PResult<Table> {
349        let start = self.expect_ident("`table`")?.span;
350        let name = self.expect_ident("テーブル名")?;
351        let (members, end) = self.member_block(BlockKind::Table)?;
352        Ok(Table {
353            name,
354            members,
355            span: start.join(end),
356        })
357    }
358
359    fn member_block(&mut self, kind: BlockKind) -> PResult<(Vec<Spanned<Member>>, Span)> {
360        if self.expect(Tok::LBrace).is_err() {
361            self.recover_block();
362            return Err(Bail);
363        }
364        let mut members = Vec::new();
365        loop {
366            self.skip_newlines();
367            if self.at(&Tok::RBrace) || self.at(&Tok::Eof) {
368                break;
369            }
370            let start = self.span();
371            match self.member(kind) {
372                Ok(m) => {
373                    let span = start.join(self.prev_span());
374                    members.push(Spanned::new(m, span));
375                    if self.end_of_statement().is_err() {
376                        self.recover_line();
377                    }
378                }
379                Err(Bail) => self.recover_line(),
380            }
381        }
382        let end = self.expect(Tok::RBrace).unwrap_or(self.prev_span());
383        Ok((members, end))
384    }
385
386    fn member(&mut self, kind: BlockKind) -> PResult<Member> {
387        let Tok::Ident(kw) = self.peek().clone() else {
388            let span = self.span();
389            let found = self.peek().describe();
390            return Err(self.error(span, format!("宣言文が必要。{found} が来た")));
391        };
392        match kw.as_str() {
393            "comment" => {
394                self.bump();
395                let text = self.expect_string("コメント(文字列)")?;
396                if kind != BlockKind::Table {
397                    self.diags.push(Diagnostic::error(
398                        text.span,
399                        "`comment` は `table` にしか書けない",
400                    ));
401                }
402                Ok(Member::Comment(text))
403            }
404            "name" => {
405                self.bump();
406                let value = self.value()?;
407                if kind != BlockKind::Table {
408                    self.diags.push(Diagnostic::error(
409                        value.span,
410                        "`name` は `table` にしか書けない",
411                    ));
412                }
413                Ok(Member::Name(value))
414            }
415            "column" => {
416                self.bump();
417                let name = self.expect_ident("カラム名")?;
418                let attrs = self.attrs()?;
419                Ok(Member::Column(Column { name, attrs }))
420            }
421            "pk" => {
422                self.bump();
423                Ok(Member::Pk(self.name_list("列名")?))
424            }
425            "index" => {
426                self.bump();
427                let columns = self.name_list("列名")?;
428                let unique = if self.at_keyword("unique") {
429                    self.bump();
430                    true
431                } else {
432                    false
433                };
434                Ok(Member::Index(Index { columns, unique }))
435            }
436            "use" => {
437                self.bump();
438                Ok(Member::Use(self.expect_ident("mixin名")?))
439            }
440            "override" => {
441                self.bump();
442                let name = self.expect_ident("カラム名")?;
443                let attrs = self.attrs()?;
444                if attrs.is_empty() {
445                    let span = name.span;
446                    return Err(self.error(span, "`override` には属性が1つ以上必要"));
447                }
448                Ok(Member::Override(Override { name, attrs }))
449            }
450            "except" => {
451                self.bump();
452                if self.at_keyword("index") {
453                    self.bump();
454                    Ok(Member::ExceptIndex(self.name_list("列名")?))
455                } else {
456                    Ok(Member::Except(self.name_list("カラム名")?))
457                }
458            }
459            "belongs_to" => self.relation(RelationKind::BelongsTo),
460            "unique_belongs_to" => self.relation(RelationKind::UniqueBelongsTo),
461            "has_many" => self.relation(RelationKind::HasMany),
462            "has_one" => self.relation(RelationKind::HasOne),
463            other => {
464                let span = self.span();
465                Err(self.error(span, format!("宣言文にならないキーワード `{other}`")))
466            }
467        }
468    }
469
470    fn relation(&mut self, kind: RelationKind) -> PResult<Member> {
471        self.bump();
472        let target = self.expect_ident("参照先の語")?;
473        let mut rel = Relation {
474            kind,
475            target,
476            fk: None,
477            alias: None,
478            via: None,
479            comment: None,
480        };
481        while let Tok::Ident(key) = self.peek().clone() {
482            if *self.peek_at(1) != Tok::Eq {
483                break;
484            }
485            let key_span = self.span();
486            self.bump();
487            self.bump();
488            if !RELATION_KEYS.contains(&key.as_str()) {
489                self.diags.push(Diagnostic::error(
490                    key_span,
491                    format!(
492                        "`{}` に書けない属性キー `{key}`。使えるのは {}",
493                        kind.keyword(),
494                        RELATION_KEYS.join(" / ")
495                    ),
496                ));
497            }
498            let duplicated = match key.as_str() {
499                "alias" => {
500                    let value = self.value()?;
501                    rel.alias.replace(value).is_some()
502                }
503                "fk" => {
504                    let value = self.expect_string("列名(文字列)")?;
505                    rel.fk.replace(value).is_some()
506                }
507                "comment" => {
508                    let value = self.expect_string("コメント(文字列)")?;
509                    rel.comment.replace(value).is_some()
510                }
511                _ => {
512                    let value = self.expect_string("列名(文字列)")?;
513                    rel.via.replace(value).is_some()
514                }
515            };
516            if duplicated {
517                self.diags.push(Diagnostic::error(
518                    key_span,
519                    format!("`{key}` が重複している"),
520                ));
521            }
522        }
523        if rel.fk.is_some() && !kind.owns_fk() {
524            let span = rel.fk.as_ref().map(|f| f.span).unwrap_or(rel.target.span);
525            self.diags.push(Diagnostic::error(
526                span,
527                format!("`{}` に `fk=` は書けない", kind.keyword()),
528            ));
529        }
530        if rel.via.is_some() && kind.owns_fk() {
531            let span = rel.via.as_ref().map(|v| v.span).unwrap_or(rel.target.span);
532            self.diags.push(Diagnostic::error(
533                span,
534                format!("`{}` に `via=` は書けない", kind.keyword()),
535            ));
536        }
537        if rel.comment.is_some() && !kind.owns_fk() {
538            let span = rel
539                .comment
540                .as_ref()
541                .map(|c| c.span)
542                .unwrap_or(rel.target.span);
543            self.diags.push(Diagnostic::error(
544                span,
545                format!(
546                    "`{}` は列を作らないので `comment=` は書けない",
547                    kind.keyword()
548                ),
549            ));
550        }
551        Ok(Member::Relation(rel))
552    }
553
554    fn attrs(&mut self) -> PResult<Vec<Attr>> {
555        let mut attrs = Vec::new();
556        while let Tok::Ident(key) = self.peek().clone() {
557            if *self.peek_at(1) != Tok::Eq {
558                break;
559            }
560            let key_span = self.span();
561            self.bump();
562            self.bump();
563            if !ATTR_KEYS.contains(&key.as_str()) {
564                self.diags.push(Diagnostic::error(
565                    key_span,
566                    format!(
567                        "知らない属性キー `{key}`。使えるのは {}",
568                        ATTR_KEYS.join(" / ")
569                    ),
570                ));
571            }
572            let value = self.value()?;
573            attrs.push(Attr {
574                key: Spanned::new(key, key_span),
575                value,
576            });
577        }
578        Ok(attrs)
579    }
580
581    /// `名前` または `[名前, ...]`
582    fn name_list(&mut self, what: &str) -> PResult<Vec<Name>> {
583        if self.eat(&Tok::LBracket) {
584            let mut names = Vec::new();
585            loop {
586                if self.at(&Tok::RBracket) {
587                    break;
588                }
589                names.push(self.expect_ident(what)?);
590                if !self.eat(&Tok::Comma) {
591                    break;
592                }
593            }
594            self.expect(Tok::RBracket)?;
595            if names.is_empty() {
596                let span = self.prev_span();
597                return Err(self.error(span, format!("{what} が空")));
598            }
599            Ok(names)
600        } else {
601            Ok(vec![self.expect_ident(what)?])
602        }
603    }
604
605    // ---------- blueprint ----------
606
607    fn blueprint_block(&mut self) -> PResult<Blueprint> {
608        let start = self.expect_ident("`blueprint`")?.span;
609        let name = self.expect_ident("blueprint名")?;
610        let mut params = Vec::new();
611        while matches!(self.peek(), Tok::Ident(_)) {
612            params.push(self.expect_ident("引数名")?);
613        }
614
615        if self.expect(Tok::LBrace).is_err() {
616            self.recover_block();
617            return Err(Bail);
618        }
619        let mut tables = Vec::new();
620        loop {
621            self.skip_newlines();
622            if self.at(&Tok::RBrace) || self.at(&Tok::Eof) {
623                break;
624            }
625            if self.at_keyword("table") {
626                match self.table_block() {
627                    Ok(t) => tables.push(t),
628                    Err(Bail) => self.recover_line(),
629                }
630            } else {
631                let span = self.span();
632                let found = self.peek().describe();
633                self.diags.push(Diagnostic::error(
634                    span,
635                    format!("blueprint 内に書けるのは `table` のみ。{found} が来た"),
636                ));
637                self.recover_line();
638            }
639        }
640        let end = self.expect(Tok::RBrace).unwrap_or(self.prev_span());
641        Ok(Blueprint {
642            name,
643            params,
644            tables,
645            span: start.join(end),
646        })
647    }
648
649    // ---------- マクロ ----------
650
651    fn macro_call(&mut self) -> PResult<MacroCall> {
652        let name = self.expect_ident("マクロ名")?;
653        let start = name.span;
654        self.expect(Tok::LParen)?;
655        let mut args = Vec::new();
656        let mut table_name = None;
657        let mut comment = None;
658        loop {
659            if self.at(&Tok::RParen) {
660                break;
661            }
662            // `キー=値` は名前付き、それ以外は位置引数。
663            if matches!(self.peek(), Tok::Ident(_)) && *self.peek_at(1) == Tok::Eq {
664                let key = self.expect_ident("キー")?;
665                self.expect(Tok::Eq)?;
666                let duplicated = match key.value.as_str() {
667                    "name" => table_name.replace(self.value()?).is_some(),
668                    "comment" => comment
669                        .replace(self.expect_string("コメント(文字列)")?)
670                        .is_some(),
671                    other => {
672                        self.diags.push(Diagnostic::error(
673                            key.span,
674                            format!("知らないキー `{other}`。使えるのは name / comment"),
675                        ));
676                        self.value()?;
677                        false
678                    }
679                };
680                if duplicated {
681                    self.diags.push(Diagnostic::error(
682                        key.span,
683                        format!("`{}` が重複している", key.value),
684                    ));
685                }
686            } else {
687                args.push(self.expect_ident("引数")?);
688            }
689            if !self.eat(&Tok::Comma) {
690                break;
691            }
692        }
693        let end = self.expect(Tok::RParen)?;
694        Ok(MacroCall {
695            name,
696            args,
697            table_name,
698            comment,
699            span: start.join(end),
700        })
701    }
702
703    // ---------- 値 ----------
704
705    fn value(&mut self) -> PResult<Spanned<Value>> {
706        let start = self.span();
707        match self.peek().clone() {
708            Tok::Str(s) => {
709                let span = self.bump().span;
710                Ok(Spanned::new(Value::Str(s), span))
711            }
712            Tok::Num(n) => {
713                let span = self.bump().span;
714                Ok(Spanned::new(Value::Num(n), span))
715            }
716            Tok::Eval(body) => {
717                let span = self.bump().span;
718                Ok(Spanned::new(Value::Eval(body), span))
719            }
720            Tok::LBracket => {
721                let items = self.value_list()?;
722                let span = start.join(self.prev_span());
723                Ok(Spanned::new(Value::List(items), span))
724            }
725            Tok::Ident(name) => {
726                if *self.peek_at(1) == Tok::LParen {
727                    let call_name = self.expect_ident("関数名")?;
728                    if !VALUE_FNS.contains(&name.as_str()) {
729                        self.diags.push(Diagnostic::error(
730                            call_name.span,
731                            format!(
732                                "値の位置で使える関数は {} のみ。`{name}` は使えない",
733                                VALUE_FNS.join(" / ")
734                            ),
735                        ));
736                    }
737                    self.expect(Tok::LParen)?;
738                    let mut args = Vec::new();
739                    loop {
740                        if self.at(&Tok::RParen) {
741                            break;
742                        }
743                        args.push(self.value()?);
744                        if !self.eat(&Tok::Comma) {
745                            break;
746                        }
747                    }
748                    let end = self.expect(Tok::RParen)?;
749                    Ok(Spanned::new(
750                        Value::Call {
751                            name: call_name,
752                            args,
753                        },
754                        start.join(end),
755                    ))
756                } else {
757                    let span = self.bump().span;
758                    Ok(Spanned::new(Value::Ident(name), span))
759                }
760            }
761            other => {
762                let span = self.span();
763                let found = other.describe();
764                Err(self.error(span, format!("値が必要。{found} が来た")))
765            }
766        }
767    }
768
769    /// リストは要素の途中で改行できる。
770    fn value_list(&mut self) -> PResult<Vec<Name>> {
771        self.expect(Tok::LBracket)?;
772        let mut items = Vec::new();
773        loop {
774            self.skip_newlines();
775            if self.at(&Tok::RBracket) || self.at(&Tok::Eof) {
776                break;
777            }
778            items.push(self.expect_ident("リスト要素")?);
779            self.skip_newlines();
780            if !self.eat(&Tok::Comma) {
781                break;
782            }
783        }
784        self.skip_newlines();
785        self.expect(Tok::RBracket)?;
786        Ok(items)
787    }
788}