// Top-level utilities: loading ATD data and sorting type definitions.
///|
fn default_warning_handler(msg : String) -> Unit {
eprintln(msg)
}
///|
/// Read ATD data from UTF-8 bytes.
///
/// - `annot_schema`: check for misplaced annotations.
/// - `expand`: perform monomorphization (`atdcat -x`).
/// - `keep_builtins`: with `expand`, preserve the builtin parametrized types.
/// - `keep_poly`: with `expand`, keep parametrized definitions (`-xk`).
/// - `xdebug`: with `expand`, keep non-standard type names (`-xd`).
/// - `inherit_fields`: expand `inherit` statements in records (`-if`).
/// - `inherit_variants`: expand `inherit` statements in sums (`-iv`).
/// - `pos_fname`: file name used in error messages.
/// - `pos_lnum`: number of the first line.
/// - `on_warning`: what to do with warnings; they are printed on stderr by
/// default.
pub fn load_bytes(
src : BytesView,
annot_schema? : Schema,
expand? : Bool = false,
keep_builtins? : Bool = false,
keep_poly? : Bool = false,
xdebug? : Bool = false,
inherit_fields? : Bool = false,
inherit_variants? : Bool = false,
pos_fname? : String = "",
pos_lnum? : Int = 1,
on_warning? : (String) -> Unit = default_warning_handler,
) -> Module raise AtdError {
let module_ = parse_module(src, pos_fname~, pos_lnum~)
check_module(module_)
let locals = load_imports(module_.imports)
check_type_refs(locals, module_.type_defs)
for w in unused_import_warnings(locals, module_.type_defs) {
on_warning(w)
}
let type_defs = if inherit_fields || inherit_variants {
expand_inherit(module_.type_defs, inherit_fields~, inherit_variants~)
} else {
module_.type_defs
}
let type_defs = if expand {
expand_type_defs(type_defs, keep_builtins~, keep_poly~, debug=xdebug)
} else {
type_defs
}
let module_ = { ..module_, type_defs, }
match annot_schema {
None => ()
Some(schema) => annot_validate(schema, Module(module_))
}
module_
}
///|
/// Read ATD data from a string. See `load_bytes` for the options.
pub fn load_string(
s : String,
annot_schema? : Schema,
expand? : Bool = false,
keep_builtins? : Bool = false,
keep_poly? : Bool = false,
xdebug? : Bool = false,
inherit_fields? : Bool = false,
inherit_variants? : Bool = false,
pos_fname? : String = "",
pos_lnum? : Int = 1,
on_warning? : (String) -> Unit = default_warning_handler,
) -> Module raise AtdError {
load_bytes(
@utf8.encode(s),
annot_schema?,
expand~,
keep_builtins~,
keep_poly~,
xdebug~,
inherit_fields~,
inherit_variants~,
pos_fname~,
pos_lnum~,
on_warning~,
)
}
///|
let ignorable_type_names : Array[TypeName] = [
TypeName::simple("unit"),
TypeName::simple("bool"),
TypeName::simple("int"),
TypeName::simple("float"),
TypeName::simple("string"),
TypeName::simple("abstract"),
]
///|
/// Topological sort for dependency analysis: split definitions into
/// mutually-recursive groups, ordered such that each group may only depend
/// on type definitions of its own group or previous groups. The boolean
/// flags indicate groups of one or more mutually recursive definitions.
///
/// `all_rec` assumes all definitions are mutually dependent.
pub fn tsort(
type_defs : Array[TypeDef],
all_rec? : Bool = false,
) -> Array[(Bool, Array[TypeDef])] {
if all_rec {
[(true, type_defs)]
} else {
let l = type_defs.map(x => {
(x, x.value.extract_type_names(ignorable=ignorable_type_names))
})
topological_sort(l, x => x.name).rev()
}
}