vrl/compiler/
compiler.rs

1use crate::compiler::expression::ExpressionError;
2use crate::compiler::expression::function_call::FunctionCallError;
3use crate::compiler::{
4    CompileConfig, Function, Program, TypeDef,
5    expression::{
6        Abort, Array, Assignment, Block, Container, Expr, Expression, FunctionArgument,
7        FunctionCall, Group, IfStatement, Literal, Noop, Not, Object, Op, Predicate, Query, Return,
8        Target, Unary, Variable, assignment, function_call, literal, predicate, query,
9    },
10    parser::ast::RootExpr,
11    program::ProgramInfo,
12};
13use crate::diagnostic::{DiagnosticList, DiagnosticMessage};
14use crate::parser::ast::{self, Node, QueryTarget};
15use crate::path::PathPrefix;
16use crate::path::{OwnedTargetPath, OwnedValuePath};
17use crate::prelude::{ArgumentList, expression};
18use crate::value::Value;
19
20use super::state::TypeState;
21
22pub(crate) type DiagnosticsMessages = Vec<Box<dyn DiagnosticMessage>>;
23
24pub struct CompilationResult {
25    pub program: Program,
26    pub warnings: DiagnosticList,
27    pub config: CompileConfig,
28}
29
30/// The compiler has many `compile_*` functions. These all accept a `state` param which
31/// should contain the type state of the program immediately before the expression
32/// that is being compiled would execute. The state should be modified to reflect the
33/// state after the compiled expression executes. This logic lives in `Expression::type_info`.
34pub struct Compiler<'a> {
35    fns: &'a [Box<dyn Function>],
36    diagnostics: DiagnosticsMessages,
37    fallible: bool,
38    abortable: bool,
39    external_queries: Vec<OwnedTargetPath>,
40    external_assignments: Vec<OwnedTargetPath>,
41
42    /// A list of variables that are missing, because the rhs expression of the
43    /// assignment failed to compile.
44    ///
45    /// This list allows us to avoid printing "undefined variable" compilation
46    /// errors when the reason for it being undefined is another compiler error.
47    skip_missing_query_target: Vec<(QueryTarget, OwnedValuePath)>,
48
49    /// Track which expression in a chain of expressions is fallible.
50    ///
51    /// It is possible for this state to switch from `None`, to `Some(T)` and
52    /// back to `None`, if the parent expression of a fallible expression
53    /// nullifies the fallibility of that expression.
54    // This should probably be kept on the call stack as the "compile_*" functions are called
55    // otherwise some expressions may remove it when they shouldn't (such as the RHS of an operation removing
56    // the error from the LHS)
57    fallible_expression_error: Option<CompilerError>,
58
59    config: CompileConfig,
60}
61
62// TODO: The diagnostic related code is in dire need of refactoring.
63// This is a workaround to avoid doing this work upfront.
64#[derive(Debug)]
65pub(crate) enum CompilerError {
66    FunctionCallError(FunctionCallError),
67    ExpressionError(ExpressionError),
68}
69
70impl CompilerError {
71    fn to_diagnostic(&self) -> &dyn DiagnosticMessage {
72        match self {
73            CompilerError::FunctionCallError(e) => e,
74            CompilerError::ExpressionError(e) => e,
75        }
76    }
77
78    fn into_diagnostic_boxed(self) -> Box<dyn DiagnosticMessage> {
79        match self {
80            CompilerError::FunctionCallError(e) => Box::new(e),
81            CompilerError::ExpressionError(e) => Box::new(e),
82        }
83    }
84}
85
86impl<'a> Compiler<'a> {
87    /// Compiles a given source into the final [`Program`].
88    ///
89    /// # Arguments
90    ///
91    /// * `source` - A string slice that holds the source code to be compiled.
92    /// * `fns` - A slice of boxed functions to be used during compilation.
93    ///
94    /// # Returns
95    ///
96    /// A `Result` containing the `CompilationResult` if successful, or a `DiagnosticList` if there are errors.
97    ///
98    /// # Errors
99    /// Any compilation error.
100    pub fn compile(
101        fns: &'a [Box<dyn Function>],
102        ast: crate::parser::Program,
103        state: &TypeState,
104        config: CompileConfig,
105    ) -> Result<CompilationResult, DiagnosticList> {
106        let initial_state = state.clone();
107        let mut state = state.clone();
108
109        let mut compiler = Self {
110            fns,
111            diagnostics: vec![],
112            fallible: false,
113            abortable: false,
114            external_queries: vec![],
115            external_assignments: vec![],
116            skip_missing_query_target: vec![],
117            fallible_expression_error: None,
118            config,
119        };
120        let expressions = compiler.compile_root_exprs(ast, &mut state);
121
122        let (errors, warnings): (Vec<_>, Vec<_>) =
123            compiler.diagnostics.into_iter().partition(|diagnostic| {
124                matches!(
125                    diagnostic.severity(),
126                    crate::diagnostic::Severity::Bug | crate::diagnostic::Severity::Error
127                )
128            });
129
130        if !errors.is_empty() {
131            return Err(errors.into());
132        }
133
134        let result = CompilationResult {
135            program: Program {
136                expressions: Block::new_inline(expressions),
137                info: ProgramInfo {
138                    fallible: compiler.fallible,
139                    abortable: compiler.abortable,
140                    target_queries: compiler.external_queries,
141                    target_assignments: compiler.external_assignments,
142                },
143                initial_state,
144            },
145            warnings: warnings.into(),
146            config: compiler.config,
147        };
148        Ok(result)
149    }
150
151    fn compile_exprs(
152        &mut self,
153        nodes: impl IntoIterator<Item = Node<ast::Expr>>,
154        state: &mut TypeState,
155    ) -> Option<Vec<Expr>> {
156        let mut exprs = vec![];
157        for node in nodes {
158            let expr = self.compile_expr(node, state)?;
159            exprs.push(expr);
160        }
161        Some(exprs)
162    }
163
164    fn compile_expr(&mut self, node: Node<ast::Expr>, state: &mut TypeState) -> Option<Expr> {
165        use ast::Expr::{
166            Abort, Assignment, Container, FunctionCall, IfStatement, Literal, Op, Query, Return,
167            Unary, Variable,
168        };
169        let original_state = state.clone();
170
171        let span = node.span();
172        let expr = match node.into_inner() {
173            Literal(node) => self.compile_literal(node, state),
174            Container(node) => self.compile_container(node, state).map(Into::into),
175            IfStatement(node) => self.compile_if_statement(node, state).map(Into::into),
176            Op(node) => self.compile_op(node, state).map(Into::into),
177            Assignment(node) => self.compile_assignment(node, state).map(Into::into),
178            Query(node) => self.compile_query(node, state).map(Into::into),
179            FunctionCall(node) => self
180                .compile_function_call(node, state)
181                .map(|function_call| {
182                    let v = function_call
183                        .warnings
184                        .iter()
185                        .cloned()
186                        .map(|w| Box::new(w) as Box<dyn DiagnosticMessage>)
187                        .collect::<Vec<_>>();
188
189                    self.diagnostics.extend(v);
190                    function_call.into()
191                }),
192            Variable(node) => self.compile_variable(node, state).map(Into::into),
193            Unary(node) => self.compile_unary(node, state).map(Into::into),
194            Abort(node) => self.compile_abort(node, state).map(Into::into),
195            Return(node) => self.compile_return(node, state).map(Into::into),
196        }?;
197
198        // If the previously compiled expression is fallible, _and_ we are
199        // currently not tracking any existing fallible expression in the chain
200        // of expressions, then this is the first expression within that chain
201        // that can cause the entire chain to be fallible.
202
203        let type_def = expr.type_info(&original_state).result;
204        if type_def.is_fallible() && self.fallible_expression_error.is_none() {
205            self.fallible_expression_error = Some(CompilerError::ExpressionError(
206                expression::ExpressionError::Fallible { span },
207            ));
208        }
209
210        Some(expr)
211    }
212
213    fn compile_literal(&mut self, node: Node<ast::Literal>, state: &mut TypeState) -> Option<Expr> {
214        use ast::Literal::{Boolean, Float, Integer, Null, RawString, Regex, String, Timestamp};
215        use bytes::Bytes;
216
217        let (span, lit) = node.take();
218
219        let literal = match lit {
220            String(template) => {
221                if let Some(v) = template.as_literal_string() {
222                    Ok(Literal::String(Bytes::from(v.to_string())))
223                } else {
224                    // Rewrite the template into an expression and compile that block.
225                    return self.compile_expr(
226                        Node::new(span, template.rewrite_to_concatenated_strings()),
227                        state,
228                    );
229                }
230            }
231            RawString(v) => Ok(Literal::String(Bytes::from(v))),
232            Integer(v) => Ok(Literal::Integer(v)),
233            Float(v) => Ok(Literal::Float(v)),
234            Boolean(v) => Ok(Literal::Boolean(v)),
235            Regex(v) => regex::Regex::new(&v)
236                .map_err(|err| literal::Error::from((span, err)))
237                .map(|r| Literal::Regex(r.into())),
238            // TODO: support more formats (similar to Vector's `Convert` logic)
239            Timestamp(v) => v
240                .parse()
241                .map(Literal::Timestamp)
242                .map_err(|err| literal::Error::from((span, err))),
243            Null => Ok(Literal::Null),
244        };
245
246        literal
247            .map(Into::into)
248            .map_err(|err| self.diagnostics.push(Box::new(err)))
249            .ok()
250    }
251
252    fn compile_container(
253        &mut self,
254        node: Node<ast::Container>,
255        state: &mut TypeState,
256    ) -> Option<Container> {
257        use ast::Container::{Array, Block, Group, Object};
258
259        let variant = match node.into_inner() {
260            Group(node) => self.compile_group(*node, state)?.into(),
261            Block(node) => self.compile_block(node, state)?.into(),
262            Array(node) => self.compile_array(node, state)?.into(),
263            Object(node) => self.compile_object(node, state)?.into(),
264        };
265
266        Some(Container::new(variant))
267    }
268
269    fn compile_group(&mut self, node: Node<ast::Group>, state: &mut TypeState) -> Option<Group> {
270        let expr = self.compile_expr(node.into_inner().into_inner(), state)?;
271
272        Some(Group::new(expr))
273    }
274
275    fn compile_root_exprs(
276        &mut self,
277        nodes: impl IntoIterator<Item = Node<ast::RootExpr>>,
278        state: &mut TypeState,
279    ) -> Vec<Expr> {
280        let mut node_exprs = vec![];
281
282        for root_expr in nodes {
283            match root_expr.into_inner() {
284                RootExpr::Expr(node_expr) => {
285                    self.fallible_expression_error = None;
286
287                    if let Some(expr) = self.compile_expr(node_expr, state) {
288                        if let Some(error) = self.fallible_expression_error.take() {
289                            self.diagnostics.push(error.into_diagnostic_boxed());
290                        }
291
292                        node_exprs.push(expr);
293                    }
294                }
295                RootExpr::Error(err) => self.handle_parser_error(err),
296            }
297        }
298
299        if node_exprs.is_empty() {
300            node_exprs.push(Expr::Noop(Noop));
301        }
302        node_exprs
303    }
304
305    fn compile_block(&mut self, node: Node<ast::Block>, state: &mut TypeState) -> Option<Block> {
306        self.compile_block_with_type(node, state)
307            .map(|(block, _type_def)| block)
308    }
309
310    fn compile_block_with_type(
311        &mut self,
312        node: Node<ast::Block>,
313        state: &mut TypeState,
314    ) -> Option<(Block, TypeDef)> {
315        let original_state = state.clone();
316        let exprs = self.compile_exprs(node.into_inner().into_iter(), state)?;
317        let block = Block::new_scoped(exprs);
318
319        // The type information from `compile_exprs` doesn't applying the "scoping" from the block.
320        // This is recalculated using the block.
321        *state = original_state;
322        let result = block.apply_type_info(state);
323        Some((block, result))
324    }
325
326    fn compile_array(&mut self, node: Node<ast::Array>, state: &mut TypeState) -> Option<Array> {
327        let exprs = self.compile_exprs(node.into_inner().into_iter(), state)?;
328
329        Some(Array::new(exprs))
330    }
331
332    fn compile_object(&mut self, node: Node<ast::Object>, state: &mut TypeState) -> Option<Object> {
333        let (keys, exprs): (Vec<String>, Vec<Option<Expr>>) = node
334            .into_inner()
335            .into_iter()
336            .map(|(k, expr)| (k.into_inner(), self.compile_expr(expr, state)))
337            .unzip();
338
339        let exprs = exprs.into_iter().collect::<Option<Vec<_>>>()?;
340
341        Some(Object::new(
342            keys.into_iter()
343                .zip(exprs)
344                .map(|(key, value)| (key.into(), value))
345                .collect(),
346        ))
347    }
348
349    fn compile_if_statement(
350        &mut self,
351        node: Node<ast::IfStatement>,
352        state: &mut TypeState,
353    ) -> Option<IfStatement> {
354        let ast::IfStatement {
355            predicate,
356            if_node,
357            else_node,
358        } = node.into_inner();
359
360        let original_state = state.clone();
361
362        let predicate = self
363            .compile_predicate(predicate, state)?
364            .map_err(|err| self.diagnostics.push(Box::new(err)))
365            .ok()?;
366
367        let after_predicate_state = state.clone();
368
369        let if_block = self.compile_block(if_node, state)?;
370
371        let else_block = if let Some(else_node) = else_node {
372            *state = after_predicate_state;
373            Some(self.compile_block(else_node, state)?)
374        } else {
375            None
376        };
377
378        let if_statement = IfStatement {
379            predicate,
380            if_block,
381            else_block,
382        };
383
384        // The current state is from one of the branches. Restore it and calculate
385        // the type state from the full "if statement" expression.
386        *state = original_state;
387        if_statement.apply_type_info(state);
388        Some(if_statement)
389    }
390
391    fn compile_predicate(
392        &mut self,
393        node: Node<ast::Predicate>,
394        state: &mut TypeState,
395    ) -> Option<predicate::Result> {
396        use ast::Predicate::{Many, One};
397
398        let (span, predicate) = node.take();
399
400        let exprs = match predicate {
401            One(node) => vec![self.compile_expr(*node, state)?],
402            Many(nodes) => self.compile_exprs(nodes, state)?,
403        };
404
405        Some(Predicate::new(
406            Node::new(span, exprs),
407            state,
408            self.fallible_expression_error
409                .as_ref()
410                .map(CompilerError::to_diagnostic),
411        ))
412    }
413
414    fn compile_op(&mut self, node: Node<ast::Op>, state: &mut TypeState) -> Option<Op> {
415        use crate::parser::ast::Opcode;
416
417        let original_state = state.clone();
418
419        let op = node.into_inner();
420        let ast::Op(lhs, opcode, rhs) = op;
421
422        let lhs_span = lhs.span();
423        let lhs = Node::new(lhs_span, self.compile_expr(*lhs, state)?);
424
425        // If we're using error-coalescing, we need to negate any tracked
426        // fallibility error state for the lhs expression.
427        if opcode.inner() == &Opcode::Err {
428            self.fallible_expression_error = None;
429        }
430
431        // save the error so the RHS can't delete an error from the LHS
432        let fallible_expression_error = self.fallible_expression_error.take();
433
434        let rhs_span = rhs.span();
435        let rhs = Node::new(rhs_span, self.compile_expr(*rhs, state)?);
436
437        let op = Op::new(lhs, opcode, rhs, state)
438            .map_err(|err| self.diagnostics.push(Box::new(err)))
439            .ok()?;
440
441        let type_info = op.type_info(&original_state);
442
443        // re-apply the RHS error saved from above
444        if self.fallible_expression_error.is_none() {
445            self.fallible_expression_error = fallible_expression_error;
446        }
447
448        if type_info.result.is_infallible() {
449            // There was a short-circuit operation that is preventing a fallibility error
450            self.fallible_expression_error = None;
451        }
452
453        // Both "lhs" and "rhs" are compiled above, but "rhs" isn't always executed.
454        // The expression can provide a more accurate type state.
455        *state = type_info.state;
456        Some(op)
457    }
458
459    /// Rewrites the ast for `a |= b` to be `a = a | b`.
460    fn rewrite_to_merge(
461        &mut self,
462        span: crate::diagnostic::Span,
463        target: &Node<ast::AssignmentTarget>,
464        expr: Box<Node<ast::Expr>>,
465        state: &mut TypeState,
466    ) -> Option<Box<Node<Expr>>> {
467        Some(Box::new(Node::new(
468            span,
469            Expr::Op(self.compile_op(
470                Node::new(
471                    span,
472                    ast::Op(
473                        Box::new(Node::new(target.span(), target.inner().to_expr(span))),
474                        Node::new(span, ast::Opcode::Merge),
475                        expr,
476                    ),
477                ),
478                state,
479            )?),
480        )))
481    }
482
483    fn compile_assignment(
484        &mut self,
485        node: Node<ast::Assignment>,
486        state: &mut TypeState,
487    ) -> Option<Assignment> {
488        use assignment::Variant;
489        use ast::{
490            Assignment::{Infallible, Single},
491            AssignmentOp,
492        };
493
494        let original_state = state.clone();
495
496        let assignment = node.into_inner();
497
498        let node = match assignment {
499            Single { target, op, expr } => {
500                let span = expr.span();
501
502                match op {
503                    AssignmentOp::Assign => {
504                        let expr = self
505                            .compile_expr(*expr, state)
506                            .map(|expr| Box::new(Node::new(span, expr)))
507                            .or_else(|| {
508                                self.skip_missing_assignment_target(&target.clone().into_inner());
509                                None
510                            })?;
511
512                        Node::new(span, Variant::Single { target, expr })
513                    }
514                    AssignmentOp::Merge => {
515                        let expr = self.rewrite_to_merge(span, &target, expr, state)?;
516                        Node::new(span, Variant::Single { target, expr })
517                    }
518                }
519            }
520            Infallible { ok, err, op, expr } => {
521                let span = expr.span();
522
523                let node = match op {
524                    AssignmentOp::Assign => {
525                        let expr = self
526                            .compile_expr(*expr, state)
527                            .map(|expr| Box::new(Node::new(span, expr)))
528                            .or_else(|| {
529                                self.skip_missing_assignment_target(&ok.clone().into_inner());
530                                self.skip_missing_assignment_target(&err.clone().into_inner());
531                                None
532                            })?;
533
534                        let node = Variant::Infallible {
535                            ok,
536                            err,
537                            expr,
538                            default: Value::Null,
539                        };
540                        Node::new(span, node)
541                    }
542                    AssignmentOp::Merge => {
543                        let expr = self.rewrite_to_merge(span, &ok, expr, state)?;
544                        let node = Variant::Infallible {
545                            ok,
546                            err,
547                            expr,
548                            default: Value::Null,
549                        };
550
551                        Node::new(span, node)
552                    }
553                };
554
555                // If the RHS expression is marked as fallible, the "infallible"
556                // assignment nullifies this fallibility, and thus no error
557                // should be emitted.
558                self.fallible_expression_error = None;
559
560                node
561            }
562        };
563
564        let assignment = Assignment::new(
565            node,
566            state,
567            self.fallible_expression_error.as_ref(),
568            &self.config,
569        )
570        .map_err(|err| self.diagnostics.push(Box::new(err)))
571        .ok()?;
572
573        // Track any potential external target assignments within the program.
574        //
575        // This data is exposed to the caller of the compiler, to allow any
576        // potential external optimizations.
577        for target in assignment.targets() {
578            if let assignment::Target::External(path) = target {
579                self.external_assignments.push(path);
580            }
581        }
582
583        // The state hasn't been updated from the actual assignment yet. Recalculate the type
584        // from the new assignment expression.
585        *state = original_state;
586        assignment.apply_type_info(state);
587
588        Some(assignment)
589    }
590
591    fn compile_query(&mut self, node: Node<ast::Query>, state: &mut TypeState) -> Option<Query> {
592        let ast::Query { target, path } = node.into_inner();
593
594        if self
595            .skip_missing_query_target
596            .contains(&(target.clone().into_inner(), path.clone().into_inner()))
597        {
598            return None;
599        }
600
601        let path = path.into_inner();
602        let target = self.compile_query_target(target, state)?;
603
604        // Track any potential external target queries within the program.
605        //
606        // This data is exposed to the caller of the compiler, to allow any
607        // potential external optimizations.
608        if let Target::External(prefix) = target {
609            let target_path = OwnedTargetPath {
610                prefix,
611                path: path.clone(),
612            };
613            self.external_queries.push(target_path);
614        }
615
616        Some(Query::new(target, path))
617    }
618
619    fn compile_query_target(
620        &mut self,
621        node: Node<ast::QueryTarget>,
622        state: &mut TypeState,
623    ) -> Option<query::Target> {
624        use ast::QueryTarget::{Container, External, FunctionCall, Internal};
625
626        let span = node.span();
627
628        let target = match node.into_inner() {
629            External(prefix) => Target::External(prefix),
630            Internal(ident) => {
631                let variable = self.compile_variable(Node::new(span, ident), state)?;
632                Target::Internal(variable)
633            }
634            Container(container) => {
635                let container = self.compile_container(Node::new(span, container), state)?;
636                Target::Container(container)
637            }
638            FunctionCall(call) => {
639                let call = self.compile_function_call(Node::new(span, call), state)?;
640                Target::FunctionCall(call)
641            }
642        };
643
644        Some(target)
645    }
646
647    #[allow(clippy::unused_self)]
648    pub(crate) fn check_function_deprecations(
649        &mut self,
650        _func: &FunctionCall,
651        _args: &ArgumentList,
652    ) {
653    }
654
655    fn compile_function_call(
656        &mut self,
657        node: Node<ast::FunctionCall>,
658        state: &mut TypeState,
659    ) -> Option<FunctionCall> {
660        let call_span = node.span();
661        let ast::FunctionCall {
662            ident,
663            abort_on_error,
664            arguments,
665            closure,
666        } = node.into_inner();
667
668        let original_state = state.clone();
669        // TODO: Remove this (hacky) code once dynamic path syntax lands.
670        //
671        // See: https://github.com/vectordotdev/vector/issues/12547
672        if ident.as_deref() == "get" {
673            self.external_queries.push(OwnedTargetPath::event_root());
674        }
675
676        let arguments: Vec<_> = arguments
677            .into_iter()
678            .map(|node| {
679                Some(Node::new(
680                    node.span(),
681                    self.compile_function_argument(node, state)?,
682                ))
683            })
684            .collect::<Option<_>>()?;
685
686        if abort_on_error {
687            self.fallible = true;
688        }
689
690        let (closure_variables, closure_block) = match closure {
691            Some(closure) => {
692                let span = closure.span();
693                let ast::FunctionClosure { variables, block } = closure.into_inner();
694                (Some(Node::new(span, variables)), Some(block))
695            }
696            None => (None, None),
697        };
698
699        // Keep track of the known scope *before* we compile the closure.
700        //
701        // This allows us to revert to any known state that the closure
702        // arguments might overwrite.
703        let local_snapshot = state.local.clone();
704
705        // TODO: The state passed into functions should be after function arguments
706        //    have resolved, but this will break many functions relying on calling `type_def`
707        //    on it's own args.
708        // see: https://github.com/vectordotdev/vector/issues/13752
709        let state_before_function = original_state.clone();
710
711        // First, we create a new function-call builder to validate the
712        // expression.
713        let function_info = function_call::Builder::new(
714            call_span,
715            ident,
716            abort_on_error,
717            arguments,
718            self.fns,
719            &state_before_function,
720            state,
721            closure_variables,
722        )
723        // Then, we compile the closure block, and compile the final
724        // function-call expression, including the attached closure.
725        .map_err(|err| self.diagnostics.push(Box::new(err)))
726        .ok()
727        .and_then(|builder| {
728            let block = match closure_block {
729                None => None,
730                Some(block) => {
731                    let span = block.span();
732                    match self.compile_block_with_type(block, state) {
733                        Some(block_with_type) => Some(Node::new(span, block_with_type)),
734                        None => return None,
735                    }
736                }
737            };
738
739            let arg_list = builder.get_arg_list().clone();
740
741            builder
742                .compile(
743                    &state_before_function,
744                    state,
745                    block,
746                    local_snapshot,
747                    &mut self.config,
748                )
749                .map_err(|err| self.diagnostics.push(Box::new(err)))
750                .ok()
751                .map(|result| {
752                    if let Some(e) = result.error {
753                        self.fallible_expression_error = Some(CompilerError::FunctionCallError(e));
754                    }
755                    (arg_list, result.function_call)
756                })
757        });
758
759        if let Some((args, function)) = &function_info {
760            self.check_function_deprecations(function, args);
761            // Update the final state using the function expression to make sure it's accurate.
762            *state = function.type_info(&original_state).state;
763        }
764
765        function_info.map(|info| info.1)
766    }
767
768    fn compile_function_argument(
769        &mut self,
770        node: Node<ast::FunctionArgument>,
771        state: &mut TypeState,
772    ) -> Option<FunctionArgument> {
773        let ast::FunctionArgument {
774            ident,
775            expr: ast_expr,
776        } = node.into_inner();
777        let span = ast_expr.span();
778        let expr = self.compile_expr(ast_expr, state)?;
779        let node = Node::new(span, expr);
780
781        Some(FunctionArgument::new(ident, node))
782    }
783
784    fn compile_variable(
785        &mut self,
786        node: Node<ast::Ident>,
787        state: &mut TypeState,
788    ) -> Option<Variable> {
789        let (span, ident) = node.take();
790
791        if self
792            .skip_missing_query_target
793            .contains(&(QueryTarget::Internal(ident.clone()), OwnedValuePath::root()))
794        {
795            return None;
796        }
797
798        Variable::new(span, ident, &state.local)
799            .map_err(|err| self.diagnostics.push(Box::new(err)))
800            .ok()
801    }
802
803    fn compile_unary(&mut self, node: Node<ast::Unary>, state: &mut TypeState) -> Option<Unary> {
804        use ast::Unary::Not;
805
806        let variant = match node.into_inner() {
807            Not(node) => self.compile_not(node, state)?.into(),
808        };
809
810        Some(Unary::new(variant))
811    }
812
813    fn compile_not(&mut self, node: Node<ast::Not>, state: &mut TypeState) -> Option<Not> {
814        let (not, expr) = node.into_inner().take();
815
816        let node = Node::new(expr.span(), self.compile_expr(*expr, state)?);
817
818        Not::new(node, not.span(), state)
819            .map_err(|err| self.diagnostics.push(Box::new(err)))
820            .ok()
821    }
822
823    fn compile_abort(&mut self, node: Node<ast::Abort>, state: &mut TypeState) -> Option<Abort> {
824        self.abortable = true;
825        let (span, abort) = node.take();
826        let message = match abort.message {
827            Some(node) => {
828                Some((*node).map_option(|expr| self.compile_expr(Node::new(span, expr), state))?)
829            }
830            None => None,
831        };
832
833        Abort::new(span, message, state)
834            .map_err(|err| self.diagnostics.push(Box::new(err)))
835            .ok()
836    }
837
838    fn compile_return(&mut self, node: Node<ast::Return>, state: &mut TypeState) -> Option<Return> {
839        let (span, r#return) = node.take();
840
841        let expr = self.compile_expr(*r#return.expr, state)?;
842        let node = Node::new(span, expr);
843
844        Return::new(span, node, state)
845            .map_err(|err| self.diagnostics.push(Box::new(err)))
846            .ok()
847    }
848
849    fn handle_parser_error(&mut self, error: crate::parser::Error) {
850        self.diagnostics.push(Box::new(error));
851    }
852
853    fn skip_missing_assignment_target(&mut self, target: &ast::AssignmentTarget) {
854        let query = match &target {
855            ast::AssignmentTarget::Noop => return,
856            ast::AssignmentTarget::Query(ast::Query { target, path }) => {
857                (target.clone().into_inner(), path.clone().into_inner())
858            }
859            ast::AssignmentTarget::Internal(ident, path) => (
860                QueryTarget::Internal(ident.clone()),
861                path.clone().unwrap_or_else(OwnedValuePath::root),
862            ),
863            ast::AssignmentTarget::External(path) => {
864                let prefix = path.as_ref().map_or(PathPrefix::Event, |x| x.prefix);
865                let path = path.clone().map_or_else(OwnedValuePath::root, |x| x.path);
866                (QueryTarget::External(prefix), path)
867            }
868        };
869
870        self.skip_missing_query_target.push(query);
871    }
872}