Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/path/lexical.nct

lexical.nct

//! Allocation-free lexical UTF-8 path queries.

see ./index.nct
see ./utf8_path.nct

instance Utf8Path {
    noalloc method &self.is_absolute(): bool {
        let value: &str = &self.text
        return leading_separator_end(value) != 0
    }

    noalloc method &self.parent(): &str? {
        let value: &str = &self.text
        let end = trailing_separator_start(value)
        if end == 0 { return none }

        let component_start = final_component_start(value, end)
        if component_start == 0 { return none }

        let root_end = leading_separator_end(value)
        var parent_end = component_start
        while parent_end > root_end && value[parent_end - 1] == 47 {
            parent_end -= 1
        }
        if parent_end == 0 { return none }
        return value.get_range(0, parent_end)?
    }

    noalloc method &self.file_name(): &str? {
        let value: &str = &self.text
        return final_component(value)?
    }

    noalloc method &self.file_stem(): &str? {
        let value: &str = &self.text
        let name = final_component(value) otherwise { return none }
        let separator = extension_separator(name) otherwise { return name }
        return name.get_range(0, separator)?
    }

    noalloc method &self.extension(): &str? {
        let value: &str = &self.text
        let name = final_component(value) otherwise { return none }
        let separator = extension_separator(name) otherwise { return none }
        return name.get_range(separator + 1, name.len())?
    }
}

noalloc func validate(value: &str): void! {
    let raw = value.bytes()
    var offset: usize = 0
    while offset < raw.len() {
        if raw[offset] == 0 {
            return invalid_path()
        }
        offset += 1
    }
    return
}

noalloc func leading_separator_end(value: &str): usize {
    var end: usize = 0
    while end < value.len() && value[end] == 47 {
        end += 1
    }
    return end
}

noalloc func trailing_separator_start(value: &str): usize {
    var end = value.len()
    while end > 0 && value[end - 1] == 47 {
        end -= 1
    }
    return end
}

noalloc func final_component_start(value: &str, end: usize): usize {
    var start = end
    while start > 0 && value[start - 1] != 47 {
        start -= 1
    }
    return start
}

noalloc func final_component(value: &str): &str? {
    let end = trailing_separator_start(value)
    if end == 0 { return none }
    let start = final_component_start(value, end)
    return value.get_range(start, end)?
}

noalloc func extension_separator(name: &str): usize? {
    if name.len() < 3 { return none }
    var found: usize = 0
    var has_extension = false
    var offset: usize = 0
    while offset < name.len() {
        if name[offset] == 46 {
            found = offset
            has_extension = true
        }
        offset += 1
    }
    if !has_extension { return none }
    if found == 0 || found + 1 == name.len() { return none }
    return found
}

noalloc func invalid_path(): error {
    return error.new("std.path.invalid_path", "UTF-8 path contains a NUL byte")
}