Skip to main content

nickel_lang_core/
parser.rs

1use crate::{
2    ast::{
3        Ast, AstAlloc,
4        compat::{FromAst, ToMainline},
5    },
6    eval::value::NickelValue,
7    position::PosTable,
8};
9
10use nickel_lang_parser::{
11    error::{ParseError, ParseErrors},
12    files::FileId,
13    identifier::LocIdent,
14    metrics,
15    position::RawSpan,
16};
17
18pub use nickel_lang_parser::{
19    ErrorTolerantParser, ExtendedTerm, FullyErrorTolerantParser, grammar,
20    grammar::{
21        CliFieldAssignmentParser, ExtendedTermParser, FixedTypeParser, StaticFieldPathParser,
22        TermParser,
23    },
24    lexer,
25};
26
27/// General interface of the various specialized Nickel parsers.
28///
29/// This trait is a compatibility layer version of [ErrorTolerantParser]. It produces data of the
30/// old, mainline types because the current pipeline still depends on them (defined in
31/// [crate::term]). Eventually we'll get rid of it and only use [ErrorTolerantParser], which
32/// produces the new AST instead.
33pub trait ErrorTolerantParserCompat<T> {
34    /// Parse a value from a lexer with the given `file_id` in an error-tolerant way. This methods
35    /// can still fail for non-recoverable errors.
36    fn parse_tolerant_compat(
37        &self,
38        pos_table: &mut PosTable,
39        file_id: FileId,
40        lexer: lexer::Lexer,
41    ) -> Result<(T, ParseErrors), ParseError>;
42
43    /// Parse a value from a lexer with the given `file_id`, failing at the first encountered
44    /// error.
45    fn parse_strict_compat(
46        &self,
47        pos_table: &mut PosTable,
48        file_id: FileId,
49        lexer: lexer::Lexer,
50    ) -> Result<T, ParseErrors>;
51}
52
53impl<'ast> FromAst<ExtendedTerm<Ast<'ast>>> for ExtendedTerm<NickelValue> {
54    fn from_ast(ast: &ExtendedTerm<Ast<'ast>>, pos_table: &mut PosTable) -> Self {
55        match ast {
56            ExtendedTerm::Term(t) => ExtendedTerm::Term(t.to_mainline(pos_table)),
57            ExtendedTerm::ToplevelLet(ident, t) => {
58                ExtendedTerm::ToplevelLet(*ident, t.to_mainline(pos_table))
59            }
60        }
61    }
62}
63
64// Generate boilerplate impl to produce legacy mainline types from the available parsers.
65macro_rules! generate_compat_impl {
66    ($parser:ty, $output:ty) => {
67        impl ErrorTolerantParserCompat<$output> for $parser {
68            fn parse_tolerant_compat(
69                &self,
70                pos_table: &mut PosTable,
71                file_id: FileId,
72                lexer: lexer::Lexer,
73            ) -> Result<($output, ParseErrors), ParseError> {
74                let alloc = AstAlloc::new();
75                self.parse_tolerant(&alloc, file_id, lexer).map(|(t, e)| {
76                    (
77                        metrics::measure_runtime!(
78                            "runtime:ast_conversion",
79                            t.to_mainline(pos_table)
80                        ),
81                        e,
82                    )
83                })
84            }
85
86            fn parse_strict_compat(
87                &self,
88                pos_table: &mut PosTable,
89                file_id: FileId,
90                lexer: lexer::Lexer,
91            ) -> Result<$output, ParseErrors> {
92                let alloc = AstAlloc::new();
93                self.parse_strict(&alloc, file_id, lexer).map(|t| {
94                    metrics::measure_runtime!("runtime:ast_conversion", t.to_mainline(pos_table))
95                })
96            }
97        }
98    };
99}
100
101generate_compat_impl!(ExtendedTermParser, ExtendedTerm<NickelValue>);
102generate_compat_impl!(TermParser, NickelValue);
103generate_compat_impl!(FixedTypeParser, crate::typ::Type);
104
105impl ErrorTolerantParserCompat<(Vec<LocIdent>, NickelValue, RawSpan)> for CliFieldAssignmentParser {
106    fn parse_tolerant_compat(
107        &self,
108        pos_table: &mut PosTable,
109        file_id: FileId,
110        lexer: lexer::Lexer,
111    ) -> Result<((Vec<LocIdent>, NickelValue, RawSpan), ParseErrors), ParseError> {
112        self.parse_tolerant(&AstAlloc::new(), file_id, lexer)
113            .map(|((path, term, span), e)| ((path, term.to_mainline(pos_table), span), e))
114    }
115
116    fn parse_strict_compat(
117        &self,
118        pos_table: &mut PosTable,
119        file_id: FileId,
120        lexer: lexer::Lexer,
121    ) -> Result<(Vec<LocIdent>, NickelValue, RawSpan), ParseErrors> {
122        self.parse_strict(&AstAlloc::new(), file_id, lexer)
123            .map(|(path, term, span)| (path, term.to_mainline(pos_table), span))
124    }
125}
126
127// This implementation doesn't do any conversion, but hide away the (useless, in this case)
128// [crate::ast::AstAlloc] parameter.
129impl ErrorTolerantParserCompat<Vec<LocIdent>> for StaticFieldPathParser {
130    fn parse_tolerant_compat(
131        &self,
132        _pos_table: &mut PosTable,
133        file_id: FileId,
134        lexer: lexer::Lexer,
135    ) -> Result<(Vec<LocIdent>, ParseErrors), ParseError> {
136        self.parse_tolerant(&AstAlloc::new(), file_id, lexer)
137    }
138
139    fn parse_strict_compat(
140        &self,
141        _pos_table: &mut PosTable,
142        file_id: FileId,
143        lexer: lexer::Lexer,
144    ) -> Result<Vec<LocIdent>, ParseErrors> {
145        self.parse_strict(&AstAlloc::new(), file_id, lexer)
146    }
147}