module generator
import (
std.alloc
std.collections
std.strings
)
let valid_identifier(value: str): bool {
if @len(value) == 0 { return false }
for byte, index in value {
let letter = (byte >= 'a' && byte <= 'z') || (byte >= 'A' && byte <= 'Z')
let digit = byte >= '0' && byte <= '9'
if !(letter || byte == '_' || (index > 0 && digit)) { return false }
}
return true
}
let parse_value(text: str): (Value, bool) {
let value = text.trim()
if @len(value) >= 2 && value[0] == '"' && value[@len(value) - 1] == '"' {
return .String(value[1:@len(value) - 1]), true
}
if value.eq("true") { return .Boolean(true), true }
if value.eq("false") { return .Boolean(false), true }
let number, valid = std.strings.parse_i64(value)
if valid { return .Number(number), true }
return .String(""), false
}
let parse_document(
engine: *mut Engine,
source: str,
path: str,
): (Document, bool) {
let mut fields = std.collections.DynamicArray(Field).init(engine.allocator)
let mut body = source
let has_front_matter = source.starts_with("---\n") || source.starts_with("---\r\n")
if !has_front_matter { return .{ fields=fields, body=body }, true }
let first_newline, _ = source.find("\n")
let mut cursor = first_newline + 1
let mut closed = false
for cursor <= @len(source) {
let relative_end, found_newline = source[cursor:].find("\n")
let end = if found_newline { cursor + relative_end } else { @len(source) }
let line = source[cursor:end].trim_end()
if line.eq("---") {
body = if found_newline { source[end + 1:] } else { "" }
closed = true
break
}
if @len(line.trim()) != 0 {
let equal, found_equal = line.find("=")
if !found_equal {
fail_path(engine, "invalid front matter in ", path)
return .{ fields=fields, body=body }, false
}
let key = line[:equal].trim()
let raw_value = line[equal + 1:].trim()
if !valid_identifier(key) {
fail_path(engine, "invalid front matter key in ", path)
return .{ fields=fields, body=body }, false
}
let value, valid = parse_value(raw_value)
if !valid {
fail_path(engine, "invalid front matter value in ", path)
return .{ fields=fields, body=body }, false
}
let mut replaced = false
for existing.&mut in fields.as_mut_slice() {
if existing.key.eq(key) {
existing.* = Field.{ key=key, value=value }
replaced = true
break
}
}
if !replaced && !fields.append(Field.{ key=key, value=value }) {
fail(engine, "out of memory")
return .{ fields=fields, body=body }, false
}
}
if !found_newline { break }
cursor = end + 1
}
if !closed {
fail_path(engine, "unterminated front matter in ", path)
return .{ fields=fields, body=body }, false
}
return .{ fields=fields, body=body }, true
}