///|
fn define_source_name(
names : Map[String, Bool],
name : String,
) -> Unit raise ParseError {
let key = name.to_lower()
if names.contains(key) {
raise ParseError::Message("duplicate name: " + name, 0)
}
names.set(key, true)
}
///|
fn source_function_name(func : Function) -> String {
match func.kind {
Constructor(name) => "[constructor]" + name
Method(name)
| AsyncMethod(name)
| MethodGetter(name)
| MethodSetter(name) =>
"[method]" + accessor_prefix(func.kind) + name + "." + func.name
Static(name)
| AsyncStatic(name)
| StaticGetter(name)
| StaticSetter(name) =>
"[static]" + accessor_prefix(func.kind) + name + "." + func.name
_ => accessor_prefix(func.kind) + func.name
}
}
///|
fn validate_source_function(func : Function) -> Unit raise ParseError {
let names : Map[String, Bool] = Map([])
match func.kind {
Method(_) | AsyncMethod(_) | MethodGetter(_) | MethodSetter(_) =>
define_source_name(names, "self")
_ => ()
}
for param in func.params {
define_source_name(names, param.name)
validate_source_type(param.ty)
}
if func.result is Some(ty) {
validate_source_type(ty)
}
}
///|
fn validate_source_type(ty : TypeExpr) -> Unit raise ParseError {
match ty {
Tuple(items) => {
if items.is_empty() {
raise ParseError::Message("empty tuple", 0)
}
for ty in items {
validate_source_type(ty)
}
}
List(inner) | FixedLengthList(inner, _) | Option(inner) =>
validate_source_type(inner)
Future(inner) | Stream(inner) =>
if inner is Some(ty) {
validate_source_type(ty)
}
Map(key, value) => {
validate_source_type(key)
validate_source_type(value)
}
Result(ok, err) => {
if ok is Some(ty) {
validate_source_type(ty)
}
if err is Some(ty) {
validate_source_type(ty)
}
}
_ => ()
}
}
///|
fn validate_source_definition(
def : TypeDef,
scope : Map[String, Bool],
) -> Unit raise ParseError {
define_source_name(scope, def.name)
let names : Map[String, Bool] = Map([])
match def.kind {
Record(fields) => {
if fields.is_empty() {
raise ParseError::Message("empty record", 0)
}
for field in fields {
define_source_name(names, field.name)
validate_source_type(field.ty)
}
}
Variant(cases) => {
if cases.is_empty() {
raise ParseError::Message("empty variant", 0)
}
for case in cases {
define_source_name(names, case.name)
if case.ty is Some(ty) {
validate_source_type(ty)
}
}
}
Enum(items) | Flags(items) => {
if items.is_empty() {
raise ParseError::Message("empty enum/flags", 0)
}
for name in items {
define_source_name(names, name)
}
}
Alias(ty) => validate_source_type(ty)
Resource(resource) =>
for func in resource.funcs {
define_source_name(scope, source_function_name(func))
validate_source_function(func)
}
}
}
///|
fn validate_source_use(
decl : UseDecl,
names : Map[String, Bool],
) -> Unit raise ParseError {
for name in decl.names {
define_source_name(names, renamed_world_item_name(name, decl.renames))
}
}
///|
fn validate_source_interface(
items : Array[InterfaceItem],
) -> Unit raise ParseError {
let names : Map[String, Bool] = Map([])
for item in items {
match item {
TypeDef(def) => validate_source_definition(def, names)
Function(func) => {
define_source_name(names, source_function_name(func))
validate_source_function(func)
}
Use(decl) => validate_source_use(decl, names)
}
}
}
///|
fn validate_source_world_item(
item : WorldItem,
names : Map[String, Bool],
) -> Unit raise ParseError {
match item {
External(_, item) | Attributed(_, item) =>
validate_source_world_item(item, names)
Function(func) => {
define_source_name(names, source_function_name(func))
validate_source_function(func)
}
InlineInterface(iface) => {
define_source_name(names, iface.name)
validate_source_interface(iface.items)
}
NamedInterface(name, _) => define_source_name(names, name)
Interface(path) =>
define_source_name(names, "[interface]" + use_path_string(path))
Use(decl) => validate_source_use(decl, names)
Include(_) => ()
}
}
///|
fn validate_source_asts(asts : Array[WitFile]) -> Unit raise ParseError {
let top_names : Map[String, Bool] = Map([])
for ast in asts {
let pkg = match ast.pkg {
Some(pkg) => pkg_key(to_rpkg(pkg))
None => ""
}
for iface in ast.interfaces {
define_source_name(top_names, pkg + "/" + iface.name)
validate_source_interface(iface.items)
}
for world in ast.worlds {
define_source_name(top_names, pkg + "/" + world.name)
let imports : Map[String, Bool] = Map([])
let exports : Map[String, Bool] = Map([])
for def in world.types {
validate_source_definition(def, imports)
}
for item in world.imports {
validate_source_world_item(item, imports)
}
for item in world.exports {
validate_source_world_item(item, exports)
}
}
}
}