Skip to main content

oak_valkyrie/parser/
mod.rs

1use crate::{ValkyrieLanguage, kind::ValkyrieSyntaxKind};
2use oak_core::{
3    GreenNode,
4    parser::{Associativity, ParserState, Pratt, binary, unary},
5    source::Source,
6};
7
8#[allow(dead_code)]
9type State<'a, S> = ParserState<'a, ValkyrieLanguage, S>;
10
11mod parse;
12
13/// A parser for the Valkyrie programming language.
14#[derive(Clone)]
15pub struct ValkyrieParser<'config> {
16    /// Language configuration
17    config: &'config ValkyrieLanguage,
18}
19
20impl<'config> ValkyrieParser<'config> {}
21
22impl<'config> ValkyrieParser<'config> {
23    /// Creates a new Valkyrie parser.
24    pub fn new(config: &'config ValkyrieLanguage) -> Self {
25        Self { config }
26    }
27}
28
29impl<'config> Pratt<ValkyrieLanguage> for ValkyrieParser<'config> {
30    fn primary<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, ValkyrieLanguage, S>) -> &'a GreenNode<'a, ValkyrieLanguage> {
31        let cp = state.checkpoint();
32        self.parse_primary(state).unwrap_or_else(|_| {
33            state.restore(cp);
34            state.bump();
35            state.finish_at(cp, ValkyrieSyntaxKind::Error)
36        })
37    }
38
39    fn infix<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, ValkyrieLanguage, S>, left: &'a GreenNode<'a, ValkyrieLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, ValkyrieLanguage>> {
40        self.skip_trivia(state);
41        let t = state.current()?;
42        let kind = t.kind;
43
44        let (prec, assoc, result_kind) = match kind {
45            // Assignment
46            ValkyrieSyntaxKind::Eq
47            | ValkyrieSyntaxKind::PlusEq
48            | ValkyrieSyntaxKind::MinusEq
49            | ValkyrieSyntaxKind::StarEq
50            | ValkyrieSyntaxKind::SlashEq
51            | ValkyrieSyntaxKind::PercentEq
52            | ValkyrieSyntaxKind::CaretEq
53            | ValkyrieSyntaxKind::AndEq
54            | ValkyrieSyntaxKind::OrEq
55            | ValkyrieSyntaxKind::ShlEq
56            | ValkyrieSyntaxKind::ShrEq => (10, Associativity::Right, ValkyrieSyntaxKind::BinaryExpression),
57
58            // Logical
59            ValkyrieSyntaxKind::OrOr => (14, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
60            ValkyrieSyntaxKind::AndAnd => (15, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
61
62            // Bitwise
63            ValkyrieSyntaxKind::Or => (20, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
64            ValkyrieSyntaxKind::Caret => (21, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
65            ValkyrieSyntaxKind::And => (22, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
66
67            // Comparison
68            ValkyrieSyntaxKind::EqEq | ValkyrieSyntaxKind::Ne => (25, Associativity::None, ValkyrieSyntaxKind::BinaryExpression),
69            ValkyrieSyntaxKind::Lt | ValkyrieSyntaxKind::Gt | ValkyrieSyntaxKind::Le | ValkyrieSyntaxKind::Ge => (30, Associativity::None, ValkyrieSyntaxKind::BinaryExpression),
70
71            // Shift
72            ValkyrieSyntaxKind::Shl | ValkyrieSyntaxKind::Shr => (35, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
73
74            // Additive
75            ValkyrieSyntaxKind::Plus | ValkyrieSyntaxKind::Minus => (40, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
76
77            // Multiplicative
78            ValkyrieSyntaxKind::Star | ValkyrieSyntaxKind::Slash | ValkyrieSyntaxKind::Percent => (50, Associativity::Left, ValkyrieSyntaxKind::BinaryExpression),
79
80            // Postfix / Access
81            ValkyrieSyntaxKind::LeftParen => (70, Associativity::Left, ValkyrieSyntaxKind::CallExpression),
82            ValkyrieSyntaxKind::Dot => (70, Associativity::Left, ValkyrieSyntaxKind::FieldExpression),
83            ValkyrieSyntaxKind::LeftBracket => (70, Associativity::Left, ValkyrieSyntaxKind::IndexExpression),
84            ValkyrieSyntaxKind::LeftBrace => (70, Associativity::Left, ValkyrieSyntaxKind::ApplyBlock),
85
86            _ => return None,
87        };
88
89        if prec < min_precedence {
90            return None;
91        }
92
93        let cp = state.checkpoint();
94        let node = match kind {
95            ValkyrieSyntaxKind::LeftParen => {
96                let cp_inner = state.checkpoint();
97                state.push_child(left);
98                state.expect(ValkyrieSyntaxKind::LeftParen).ok();
99                while let Some(t) = state.current() {
100                    if t.kind == ValkyrieSyntaxKind::RightParen {
101                        break;
102                    }
103                    let arg = self.parse_expression_internal(state, 0);
104                    state.push_child(arg);
105                    if state.at(ValkyrieSyntaxKind::Comma) {
106                        state.expect(ValkyrieSyntaxKind::Comma).ok();
107                    }
108                }
109                state.expect(ValkyrieSyntaxKind::RightParen).ok();
110                state.finish_at(cp_inner, ValkyrieSyntaxKind::CallExpression)
111            }
112            ValkyrieSyntaxKind::LeftBracket => {
113                let cp_inner = state.checkpoint();
114                state.push_child(left);
115                state.expect(ValkyrieSyntaxKind::LeftBracket).ok();
116                let index = self.parse_expression_internal(state, 0);
117                state.push_child(index);
118                state.expect(ValkyrieSyntaxKind::RightBracket).ok();
119                state.finish_at(cp_inner, ValkyrieSyntaxKind::IndexExpression)
120            }
121            ValkyrieSyntaxKind::LeftBrace => {
122                let cp_inner = state.checkpoint();
123                state.push_child(left);
124                self.parse_block_expr_node(state).ok();
125                state.finish_at(cp_inner, ValkyrieSyntaxKind::ApplyBlock)
126            }
127            _ => binary(state, left, kind, prec, assoc, result_kind, |s, p| self.parse_expression_internal(s, p)),
128        };
129        Some(node)
130    }
131
132    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut ParserState<'a, ValkyrieLanguage, S>) -> &'a GreenNode<'a, ValkyrieLanguage> {
133        self.skip_trivia(state);
134        let t = match state.current() {
135            Some(t) => t,
136            None => return self.primary(state),
137        };
138
139        let kind = t.kind;
140        let (prec, result_kind) = match kind {
141            ValkyrieSyntaxKind::Not | ValkyrieSyntaxKind::Minus | ValkyrieSyntaxKind::Plus | ValkyrieSyntaxKind::Star | ValkyrieSyntaxKind::And => (60, ValkyrieSyntaxKind::UnaryExpression),
142            _ => return self.primary(state),
143        };
144
145        let cp = state.checkpoint();
146        unary(state, kind, prec, result_kind, |s, p| self.parse_expression_internal(s, p))
147    }
148}
149
150impl<'config> oak_core::parser::Parser<ValkyrieLanguage> for ValkyrieParser<'config> {
151    fn parse<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[oak_core::TextEdit], cache: &'a mut impl oak_core::parser::ParseCache<ValkyrieLanguage>) -> oak_core::ParseOutput<'a, ValkyrieLanguage> {
152        let lexer = crate::lexer::ValkyrieLexer::new(self.config);
153        oak_core::parser::parse_with_lexer(&lexer, source, edits, cache, |state| Ok(self.parse_root_internal(state)))
154    }
155}