Skip to main content

oak_dockerfile/parser/
mod.rs

1/// Element types and tree structure definitions for Dockerfile.
2pub mod element_type;
3
4use crate::{language::DockerfileLanguage, lexer::DockerfileLexer, parser::element_type::DockerfileElementType};
5use oak_core::{
6    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
7    source::{Source, TextEdit},
8};
9
10/// Parser for Dockerfile.
11pub struct DockerfileParser<'config> {
12    pub(crate) config: &'config DockerfileLanguage,
13}
14
15impl<'config> DockerfileParser<'config> {
16    /// Creates a new `DockerfileParser` with the given language configuration.
17    pub fn new(config: &'config DockerfileLanguage) -> Self {
18        Self { config }
19    }
20}
21
22impl<'config> Parser<DockerfileLanguage> for DockerfileParser<'config> {
23    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<DockerfileLanguage>) -> ParseOutput<'a, DockerfileLanguage> {
24        let lexer = DockerfileLexer::new(&self.config);
25        parse_with_lexer(&lexer, text, edits, cache, |state| {
26            let root_checkpoint = state.checkpoint();
27            while state.not_at_end() {
28                if state.at(crate::lexer::token_type::DockerfileTokenType::Whitespace) || state.at(crate::lexer::token_type::DockerfileTokenType::Newline) {
29                    state.advance();
30                    continue;
31                }
32
33                if state.at(crate::lexer::token_type::DockerfileTokenType::Comment) {
34                    let checkpoint = state.checkpoint();
35                    state.advance();
36                    state.finish_at(checkpoint, DockerfileElementType::Comment);
37                    continue;
38                }
39
40                // Parse instructions
41                let instruction_checkpoint = state.checkpoint();
42                let kind = match state.peek_kind() {
43                    Some(crate::lexer::token_type::DockerfileTokenType::From) => Some(DockerfileElementType::From),
44                    Some(crate::lexer::token_type::DockerfileTokenType::Run) => Some(DockerfileElementType::Run),
45                    Some(crate::lexer::token_type::DockerfileTokenType::Cmd) => Some(DockerfileElementType::Cmd),
46                    Some(crate::lexer::token_type::DockerfileTokenType::Label) => Some(DockerfileElementType::Label),
47                    Some(crate::lexer::token_type::DockerfileTokenType::Expose) => Some(DockerfileElementType::Expose),
48                    Some(crate::lexer::token_type::DockerfileTokenType::Env) => Some(DockerfileElementType::Env),
49                    Some(crate::lexer::token_type::DockerfileTokenType::Add) => Some(DockerfileElementType::Add),
50                    Some(crate::lexer::token_type::DockerfileTokenType::Copy) => Some(DockerfileElementType::Copy),
51                    Some(crate::lexer::token_type::DockerfileTokenType::Entrypoint) => Some(DockerfileElementType::Entrypoint),
52                    Some(crate::lexer::token_type::DockerfileTokenType::Volume) => Some(DockerfileElementType::Volume),
53                    Some(crate::lexer::token_type::DockerfileTokenType::User) => Some(DockerfileElementType::User),
54                    Some(crate::lexer::token_type::DockerfileTokenType::Workdir) => Some(DockerfileElementType::Workdir),
55                    Some(crate::lexer::token_type::DockerfileTokenType::Arg) => Some(DockerfileElementType::Arg),
56                    Some(crate::lexer::token_type::DockerfileTokenType::Onbuild) => Some(DockerfileElementType::Onbuild),
57                    Some(crate::lexer::token_type::DockerfileTokenType::Stopsignal) => Some(DockerfileElementType::Stopsignal),
58                    Some(crate::lexer::token_type::DockerfileTokenType::Healthcheck) => Some(DockerfileElementType::Healthcheck),
59                    Some(crate::lexer::token_type::DockerfileTokenType::Shell) => Some(DockerfileElementType::Shell),
60                    Some(crate::lexer::token_type::DockerfileTokenType::Maintainer) => Some(DockerfileElementType::Maintainer),
61                    _ => None,
62                };
63
64                if let Some(kind) = kind {
65                    state.advance(); // consume instruction keyword
66                    // Consume until newline or EOF
67                    while state.not_at_end() && !state.at(crate::lexer::token_type::DockerfileTokenType::Newline) {
68                        state.advance();
69                    }
70                    state.finish_at(instruction_checkpoint, kind);
71                }
72                else {
73                    // Unknown instruction or something else
74                    state.advance();
75                }
76            }
77            Ok(state.finish_at(root_checkpoint, DockerfileElementType::Root))
78        })
79    }
80}