///|
fn join_path(dir : String, entry : String) -> String {
if dir == "" || dir == "." {
entry
} else if dir.has_suffix("/") {
dir + entry
} else {
dir + "/" + entry
}
}
///|
fn parent_dir(path : String) -> String {
let chars = path.to_array()
let mut last_slash = -1
for i, c in chars {
if c == '/' {
last_slash = i
}
}
if last_slash < 0 {
"."
} else if last_slash == 0 {
"/"
} else {
String::from_array(chars[:last_slash])
}
}
///|
fn sort_strings(items : Array[String]) -> Unit {
let len = items.length()
if len < 2 {
return
}
for i in 1..= 0 && items[j].compare(key) > 0 {
items[j + 1] = items[j]
j = j - 1
}
items[j + 1] = key
}
}
///|
fn read_dir_raise(dir : String) -> Array[String] raise ParseError {
@fs.read_dir(dir) catch {
e =>
raise ParseError::Message(
"read_dir failed: " + dir + " (" + e.to_string() + ")",
0,
)
}
}
///|
fn read_file_raise(path : String) -> String raise ParseError {
@fs.read_file_to_string(path) catch {
e =>
raise ParseError::Message(
"read failed: " + path + " (" + e.to_string() + ")",
0,
)
}
}
///|
fn parse_file_raise(path : String) -> WitFile raise ParseError {
let source = read_file_raise(path)
parse_wit(source) catch {
ParseError::Message(msg, offset) =>
raise ParseError::Message(path + ": " + msg, offset)
}
}
///|
fn merge_wit_files(
base : WitFile,
next : WitFile,
path : String,
) -> WitFile raise ParseError {
let pkg = match (base.pkg, next.pkg) {
(Option::None, Option::None) => Option::None
(Option::Some(p), Option::None) => Option::Some(p)
(Option::None, Option::Some(p)) => Option::Some(p)
(Option::Some(a), Option::Some(b)) =>
if a == b {
Option::Some(a)
} else {
raise ParseError::Message("package mismatch in " + path, 0)
}
}
let interfaces = base.interfaces
for iface in next.interfaces {
interfaces.push(iface)
}
let worlds = base.worlds
for world in next.worlds {
worlds.push(world)
}
{ pkg, interfaces, worlds }
}
///|
fn parse_dir_raise(dir : String) -> WitFile raise ParseError {
let entries = read_dir_raise(dir)
let files : Array[String] = []
for entry in entries {
if entry == "deps" {
continue
}
let path = join_path(dir, entry)
let is_file = @fs.is_file(path) catch { _ => false }
if is_file && entry.has_suffix(".wit") {
files.push(path)
}
}
sort_strings(files)
if files.length() == 0 {
raise ParseError::Message("no .wit files in " + dir, 0)
}
let mut merged : WitFile? = None
for path in files {
let parsed = parse_file_raise(path)
merged = match merged {
None => Some(parsed)
Some(current) => Some(merge_wit_files(current, parsed, path))
}
}
match merged {
Some(result) => result
None => raise ParseError::Message("no .wit files in " + dir, 0)
}
}
///|
fn parse_path_raise(path : String) -> WitFile raise ParseError {
if not(@fs.path_exists(path)) {
raise ParseError::Message("path not found: " + path, 0)
}
let is_dir = @fs.is_dir(path) catch { _ => false }
if is_dir {
parse_dir_raise(path)
} else {
parse_file_raise(path)
}
}
///|
fn load_deps(wit_dir : String) -> Array[WitFile] raise ParseError {
let deps_dir = join_path(wit_dir, "deps")
if not(@fs.path_exists(deps_dir)) {
return []
}
let is_dir = @fs.is_dir(deps_dir) catch { _ => false }
if not(is_dir) {
return []
}
let entries = read_dir_raise(deps_dir)
let deps : Array[WitFile] = []
let len = entries.length()
for i in 0.. false }
if dep_is_dir {
deps.push(parse_dir_raise(dep_path))
}
}
deps
}
///|
fn find_world_id(
resolve : Resolve,
main_pkg : PackageName?,
main_worlds : Array[World],
world : String?,
) -> Int raise ParseError {
let target_name = match world {
Option::Some(name) => name
Option::None =>
if main_worlds.length() > 0 {
main_worlds[0].name
} else {
raise ParseError::Message("no world found in WIT", 0)
}
}
match main_pkg {
Option::Some(pkg) => {
let target_pkg = to_rpkg(pkg)
let mut pkg_id : Int? = Option::None
for i, item in resolve.packages {
if item.name == target_pkg {
pkg_id = Option::Some(i)
break
}
}
guard pkg_id is Option::Some(pid) else {
raise ParseError::Message(
"package not found for world: " + target_name,
0,
)
}
let pkg_def = resolve.packages[pid]
match pkg_def.worlds.get(target_name) {
Option::Some(id) => id
Option::None =>
raise ParseError::Message("world not found: " + target_name, 0)
}
}
Option::None =>
if world is Option::Some(_) {
for i, item in resolve.worlds {
if item.name == target_name {
return i
}
}
raise ParseError::Message("world not found: " + target_name, 0)
} else {
if resolve.worlds.length() == 0 {
raise ParseError::Message("no world found in WIT", 0)
}
0
}
}
}
///|
fn resolve_path_raise(
path : String,
world : String?,
) -> ResolveInput raise ParseError {
let main_ast = parse_path_raise(path)
let is_dir = @fs.is_dir(path) catch { _ => false }
let wit_dir = if is_dir { path } else { parent_dir(path) }
let deps = load_deps(wit_dir)
let asts = deps
asts.push(main_ast)
let resolve = resolve_asts(asts)
let world_id = find_world_id(resolve, main_ast.pkg, main_ast.worlds, world)
{ resolve, world_id }
}