// epm/src/manifest.el — vessel and package manifest parser // // Parses manifest.el files in the vessel/package format: // // vessel "el-auth" { // version "0.1.0" // description "..." // authors ["..."] // edition "2026" // } // // dependencies { // el-platform "1.0" // el-identity "0.1" // } // // build { // entry "src/main.el" // output "dist/" // } // // Parsing is line-by-line string extraction, same approach as elb.el. // No AST, no grammar — sufficient for well-formed manifests. // ── String extraction helpers ───────────────────────────────────────────────── // extract_quoted extracts the first double-quoted token from a string. // Returns "" if no quoted token is found. // Example: extract_quoted("vessel \"el-auth\" {") -> "el-auth" fn extract_quoted(s: String) -> String { let first: Int = str_index_of(s, "\"") if first < 0 { return "" } let after: String = str_slice(s, first + 1, str_len(s)) let close: Int = str_index_of(after, "\"") if close < 0 { return "" } return str_slice(after, 0, close) } // ── Manifest field extraction ───────────────────────────────────────────────── // manifest_name extracts the vessel or package name from manifest source. // Recognises both "vessel" and "package" block headers. fn manifest_name(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) if str_starts_with(t, "vessel ") { return extract_quoted(t) } if str_starts_with(t, "package ") { return extract_quoted(t) } let i = i + 1 } return "" } // manifest_version extracts the version string from manifest source. fn manifest_version(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) if str_starts_with(t, "version ") { return extract_quoted(t) } let i = i + 1 } return "" } // manifest_description extracts the description string from manifest source. fn manifest_description(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) if str_starts_with(t, "description ") { return extract_quoted(t) } let i = i + 1 } return "" } // manifest_entry extracts the build entry file from manifest source. fn manifest_entry(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) if str_starts_with(t, "entry ") { return extract_quoted(t) } let i = i + 1 } return "" } // manifest_deps extracts dependencies as a JSON array of {"name":..., "version":...} objects. // // Scans lines inside the dependencies { ... } block. // Each dep line looks like: // el-platform "1.0" // // Returns a JSON array string, e.g.: // [{"name":"el-platform","version":"1.0"},{"name":"el-identity","version":"0.1"}] fn manifest_deps(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let in_deps: Bool = false let result: String = "[" let count: Int = 0 let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) // Detect entry into dependencies block if str_eq(t, "dependencies {") { let in_deps = true let i = i + 1 } else { if in_deps { // Detect exit from block if str_eq(t, "}") { let in_deps = false } else { // Each dep line: name "version" // Find first space to split name from quoted version let sp: Int = str_index_of(t, " ") if sp > 0 { let dep_name: String = str_slice(t, 0, sp) let rest: String = str_trim(str_slice(t, sp, str_len(t))) let dep_ver: String = extract_quoted(rest) if !str_eq(dep_name, "") { let entry: String = "{\"name\":\"" + dep_name + "\",\"version\":\"" + dep_ver + "\"}" if count == 0 { let result = result + entry } else { let result = result + "," + entry } let count = count + 1 } } } } let i = i + 1 } } return result + "]" } // manifest_is_vessel returns true if the manifest declares a vessel (not a package). fn manifest_is_vessel(src: String) -> Bool { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) let i: Int = 0 while i < n { let line: String = native_list_get(lines, i) let t: String = str_trim(line) if str_starts_with(t, "vessel ") { return true } let i = i + 1 } return false }