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