only-semantic 0.0.4

Semantic analysis for the only task language.
Documentation
use smol_str::SmolStr;
use text_size::TextRange;

use crate::InterpolationAst;

pub(crate) fn scan_interpolations(text: &str) -> Vec<InterpolationAst> {
    let mut out = Vec::new();
    let mut offset = 0usize;

    while let Some(start) = text[offset..].find("{{") {
        let open = offset + start;

        if marker_is_escaped(text, open) {
            offset = open + 2;
            continue;
        }

        let Some(end_rel) = text[open + 2..].find("}}") else {
            break;
        };
        let close = open + 2 + end_rel + 2;
        let name = text[open + 2..close - 2].trim();
        out.push(InterpolationAst {
            name: SmolStr::new(name),
            range: TextRange::new((open as u32).into(), (close as u32).into()),
        });
        offset = close;
    }

    out
}

fn marker_is_escaped(text: &str, marker_start: usize) -> bool {
    let mut slash_count = 0usize;
    let bytes = text.as_bytes();
    let mut index = marker_start;

    while index > 0 && bytes[index - 1] == b'\\' {
        slash_count += 1;
        index -= 1;
    }

    slash_count % 2 == 1
}