harn_parser/
interpolation.rs1use harn_lexer::Lexer;
10
11use crate::operators::OperatorGrammar;
12use crate::{Parser, SNode};
13
14pub fn parse_expression(
22 source: Option<&str>,
23 segment: &str,
24 line: usize,
25 column: usize,
26) -> Option<SNode> {
27 parse_expression_with_grammar(OperatorGrammar::CURRENT, source, segment, line, column)
28}
29
30pub fn parse_expression_with_grammar(
33 grammar: OperatorGrammar,
34 source: Option<&str>,
35 segment: &str,
36 line: usize,
37 column: usize,
38) -> Option<SNode> {
39 Parser::new(lexer(source, segment, line, column).tokenize().ok()?)
40 .with_operator_grammar(grammar)
41 .parse_single_expression()
42 .ok()
43}
44
45pub fn lexer<'seg>(
47 source: Option<&str>,
48 segment: &'seg str,
49 line: usize,
50 column: usize,
51) -> Lexer<'seg> {
52 let offset = source
53 .and_then(|source| harn_lexer::byte_offset_for_position(source, line, column))
54 .unwrap_or_default();
55 Lexer::with_position_and_offset(segment, line, column, offset)
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::Node;
62
63 #[test]
64 #[expect(
65 clippy::string_slice,
66 reason = "expression spans come from the lexer and lie on char boundaries"
67 )]
68 fn parses_a_hole_into_spans_that_address_the_containing_source() {
69 let source = "const label = \"é ${platform()}\"\n";
70
71 let expression = parse_expression(Some(source), "platform()", 1, 20).expect("expression");
72
73 assert!(matches!(expression.node, Node::FunctionCall { .. }));
74 assert_eq!(
75 &source[expression.span.start..expression.span.end],
76 "platform()"
77 );
78 }
79}