Current section

Files

Jump to
caffeine_lang src caffeine_lang@frontend@validator.erl
Raw

src/caffeine_lang@frontend@validator.erl

-module(caffeine_lang@frontend@validator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/frontend/validator.gleam").
-export([error_to_string/1, validate_measurements_file/1, validate_expects_file/1]).
-export_type([validator_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type validator_error() :: {duplicate_extendable, binary()} |
{undefined_extendable, binary(), binary(), list(binary())} |
{duplicate_extends_reference, binary(), binary()} |
{invalid_extendable_kind, binary(), binary(), binary()} |
{undefined_type_alias, binary(), binary(), list(binary())} |
{duplicate_type_alias, binary()} |
{circular_type_alias, binary(), list(binary())} |
{invalid_dict_key_type_alias, binary(), binary(), binary()} |
{extendable_overshadowing, binary(), binary(), binary()} |
{extendable_type_alias_name_collision, binary()} |
{invalid_refinement_value, binary(), binary(), binary()} |
{invalid_percentage_bounds, binary(), binary()}.
-file("src/caffeine_lang/frontend/validator.gleam", 62).
?DOC(false).
-spec error_to_string(validator_error()) -> binary().
error_to_string(Err) ->
case Err of
{duplicate_extendable, Name} ->
<<"Duplicate extendable: "/utf8, Name/binary>>;
{undefined_extendable, Name@1, Referenced_by, _} ->
<<<<<<<<"Undefined extendable '"/utf8, Name@1/binary>>/binary,
"' referenced by '"/utf8>>/binary,
Referenced_by/binary>>/binary,
"'"/utf8>>;
{duplicate_extends_reference, Name@2, Referenced_by@1} ->
<<<<<<<<"Duplicate extends reference '"/utf8, Name@2/binary>>/binary,
"' in '"/utf8>>/binary,
Referenced_by@1/binary>>/binary,
"'"/utf8>>;
{invalid_extendable_kind, Name@3, Expected, Got} ->
<<<<<<<<<<"Invalid extendable kind for '"/utf8, Name@3/binary>>/binary,
"': expected "/utf8>>/binary,
Expected/binary>>/binary,
", got "/utf8>>/binary,
Got/binary>>;
{undefined_type_alias, Name@4, Referenced_by@2, _} ->
<<<<<<<<"Undefined type alias '"/utf8, Name@4/binary>>/binary,
"' referenced by '"/utf8>>/binary,
Referenced_by@2/binary>>/binary,
"'"/utf8>>;
{duplicate_type_alias, Name@5} ->
<<"Duplicate type alias: "/utf8, Name@5/binary>>;
{circular_type_alias, Name@6, _} ->
<<<<"Circular type alias reference detected in '"/utf8,
Name@6/binary>>/binary,
"'"/utf8>>;
{invalid_dict_key_type_alias, Alias_name, Resolved_to, Referenced_by@3} ->
<<<<<<<<<<<<"Type alias '"/utf8, Alias_name/binary>>/binary,
"' used as Dict key resolves to '"/utf8>>/binary,
Resolved_to/binary>>/binary,
"' which is not String-based, in '"/utf8>>/binary,
Referenced_by@3/binary>>/binary,
"'"/utf8>>;
{extendable_overshadowing, Field_name, Item_name, Extendable_name} ->
<<<<<<<<<<<<"Field '"/utf8, Field_name/binary>>/binary,
"' in '"/utf8>>/binary,
Item_name/binary>>/binary,
"' overshadows field from extendable '"/utf8>>/binary,
Extendable_name/binary>>/binary,
"'"/utf8>>;
{extendable_type_alias_name_collision, Name@7} ->
<<<<"Name '"/utf8, Name@7/binary>>/binary,
"' is used as both an extendable and a type alias"/utf8>>;
{invalid_refinement_value, Value, Expected_type, Referenced_by@4} ->
<<<<<<<<<<<<"Refinement value '"/utf8, Value/binary>>/binary,
"' is not a valid "/utf8>>/binary,
Expected_type/binary>>/binary,
" literal, in '"/utf8>>/binary,
Referenced_by@4/binary>>/binary,
"'"/utf8>>;
{invalid_percentage_bounds, Value@1, Referenced_by@5} ->
<<<<<<<<"Percentage value '"/utf8, Value@1/binary>>/binary,
"' must be between 0.0 and 100.0, in '"/utf8>>/binary,
Referenced_by@5/binary>>/binary,
"'"/utf8>>
end.
-file("src/caffeine_lang/frontend/validator.gleam", 834).
?DOC(
" Guards against accumulated errors. If errors is non-empty, returns them;\n"
" otherwise continues with the provided callback.\n"
).
-spec guard_errors(
list(validator_error()),
fun(() -> {ok, NJR} | {error, list(validator_error())})
) -> {ok, NJR} | {error, list(validator_error())}.
guard_errors(Errors, Otherwise) ->
case Errors of
[] ->
Otherwise();
_ ->
{error, Errors}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 818).
?DOC(" Converts a single-error Result to a list of errors (empty on Ok).\n").
-spec errors_to_list({ok, nil} | {error, validator_error()}) -> list(validator_error()).
errors_to_list(Result) ->
case Result of
{ok, _} ->
[];
{error, Err} ->
[Err]
end.
-file("src/caffeine_lang/frontend/validator.gleam", 432).
?DOC(" Validates that an item's field sets don't overshadow fields from its extended extendables.\n").
-spec validate_no_overshadowing(
binary(),
list(binary()),
list(gleam@set:set(binary())),
gleam@dict:dict(binary(), gleam@set:set(binary()))
) -> {ok, nil} | {error, validator_error()}.
validate_no_overshadowing(Item_name, Extends, Field_sets, Extendable_map) ->
gleam@list:try_each(
Extends,
fun(Ext_name) -> case gleam_stdlib:map_get(Extendable_map, Ext_name) of
{error, _} ->
{ok, nil};
{ok, Ext_fields} ->
gleam@list:try_each(
Field_sets,
fun(Item_fields) ->
case begin
_pipe = gleam@set:intersection(
Item_fields,
Ext_fields
),
gleam@set:to_list(_pipe)
end of
[] ->
{ok, nil};
[Field | _] ->
{error,
{extendable_overshadowing,
Field,
Item_name,
Ext_name}}
end
end
)
end end
).
-file("src/caffeine_lang/frontend/validator.gleam", 392).
-spec validate_no_duplicate_extends_loop(
list(binary()),
binary(),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicate_extends_loop(Extends, Item_name, Seen) ->
case Extends of
[] ->
{ok, nil};
[First | Rest] ->
gleam@bool:guard(
gleam@set:contains(Seen, First),
{error, {duplicate_extends_reference, First, Item_name}},
fun() ->
validate_no_duplicate_extends_loop(
Rest,
Item_name,
gleam@set:insert(Seen, First)
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 385).
?DOC(" Validates that no extendable is referenced twice in the same extends list.\n").
-spec validate_no_duplicate_extends(list(binary()), binary()) -> {ok, nil} |
{error, validator_error()}.
validate_no_duplicate_extends(Extends, Item_name) ->
validate_no_duplicate_extends_loop(Extends, Item_name, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 363).
?DOC(" Validates that all names in extends list exist as extendables.\n").
-spec validate_extends_exist(list(binary()), binary(), gleam@set:set(binary())) -> {ok,
nil} |
{error, validator_error()}.
validate_extends_exist(Extends, Item_name, Extendable_names) ->
case Extends of
[] ->
{ok, nil};
[First | Rest] ->
gleam@bool:guard(
not gleam@set:contains(Extendable_names, First),
{error,
{undefined_extendable,
First,
Item_name,
gleam@set:to_list(Extendable_names)}},
fun() ->
validate_extends_exist(Rest, Item_name, Extendable_names)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 417).
?DOC(" Builds a map of extendable name to its field names for overshadowing checks.\n").
-spec build_extendable_field_map(list(caffeine_lang@frontend@ast:extendable())) -> gleam@dict:dict(binary(), gleam@set:set(binary())).
build_extendable_field_map(Extendables) ->
_pipe = Extendables,
_pipe@3 = gleam@list:map(
_pipe,
fun(E) ->
Field_names = begin
_pipe@1 = erlang:element(2, erlang:element(4, E)),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@2)
end,
{erlang:element(2, E), Field_names}
end
),
maps:from_list(_pipe@3).
-file("src/caffeine_lang/frontend/validator.gleam", 295).
?DOC(" Validates extends references for measurement items.\n").
-spec validate_measurement_items_extends(
list(caffeine_lang@frontend@ast:measurement_item()),
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_measurement_items_extends(Items, Extendables) ->
Extendable_names = begin
_pipe = Extendables,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> erlang:element(2, E) end),
gleam@set:from_list(_pipe@1)
end,
Extendable_map = build_extendable_field_map(Extendables),
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(4, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
gleam@result:'try'(
validate_no_duplicate_extends(
erlang:element(4, Item),
erlang:element(2, Item)
),
fun(_) ->
Requires_fields = begin
_pipe@2 = erlang:element(
2,
erlang:element(5, Item)
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@3)
end,
Provides_fields = begin
_pipe@4 = erlang:element(
2,
erlang:element(6, Item)
),
_pipe@5 = gleam@list:map(
_pipe@4,
fun(F@1) -> erlang:element(2, F@1) end
),
gleam@set:from_list(_pipe@5)
end,
validate_no_overshadowing(
erlang:element(2, Item),
erlang:element(4, Item),
[Requires_fields, Provides_fields],
Extendable_map
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 750).
?DOC(" Validates that a string value is valid for the given primitive type.\n").
-spec validate_string_matches_primitive(
binary(),
caffeine_lang@types:primitive_types(),
binary()
) -> {ok, nil} | {error, validator_error()}.
validate_string_matches_primitive(Value, Primitive, Context_name) ->
Is_valid = case Primitive of
string ->
true;
{semantic_type, _} ->
true;
boolean ->
(Value =:= <<"true"/utf8>>) orelse (Value =:= <<"false"/utf8>>);
{numeric_type, integer} ->
gleam@result:is_ok(gleam_stdlib:parse_int(Value));
{numeric_type, float} ->
gleam@result:is_ok(gleam_stdlib:parse_float(Value));
{numeric_type, percentage} ->
Raw = case gleam_stdlib:string_ends_with(Value, <<"%"/utf8>>) of
true ->
gleam@string:drop_end(Value, 1);
false ->
Value
end,
case gleam_stdlib:parse_float(Raw) of
{ok, F} ->
gleam@bool:guard(
(F < +0.0) orelse (F > 100.0),
(false),
fun() -> true end
);
{error, _} ->
false
end
end,
case Is_valid of
true ->
{ok, nil};
false ->
case Primitive of
{numeric_type, percentage} ->
Raw@1 = case gleam_stdlib:string_ends_with(
Value,
<<"%"/utf8>>
) of
true ->
gleam@string:drop_end(Value, 1);
false ->
Value
end,
case gleam_stdlib:parse_float(Raw@1) of
{ok, _} ->
{error,
{invalid_percentage_bounds, Value, Context_name}};
{error, _} ->
{error,
{invalid_refinement_value,
Value,
caffeine_lang@types:primitive_type_to_string(
Primitive
),
Context_name}}
end;
_ ->
{error,
{invalid_refinement_value,
Value,
caffeine_lang@types:primitive_type_to_string(
Primitive
),
Context_name}}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 739).
?DOC(" Extracts the primitive type from a ParsedType, unwrapping Defaulted modifiers.\n").
-spec extract_primitive_from_parsed(caffeine_lang@types:parsed_type()) -> {ok,
caffeine_lang@types:primitive_types()} |
{error, nil}.
extract_primitive_from_parsed(Typ) ->
case Typ of
{parsed_primitive, Primitive} ->
{ok, Primitive};
{parsed_modifier, {defaulted, Inner, _}} ->
extract_primitive_from_parsed(Inner);
_ ->
{error, nil}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 706).
?DOC(" Validates that refinement string values match the declared primitive type.\n").
-spec validate_refinement_values(
caffeine_lang@types:refinement_types(caffeine_lang@types:parsed_type()),
binary()
) -> {ok, nil} | {error, validator_error()}.
validate_refinement_values(Refinement, Context_name) ->
case Refinement of
{one_of, Inner, Values} ->
case extract_primitive_from_parsed(Inner) of
{ok, Primitive} ->
_pipe = Values,
_pipe@1 = gleam@set:to_list(_pipe),
gleam@list:try_each(
_pipe@1,
fun(Value) ->
validate_string_matches_primitive(
Value,
Primitive,
Context_name
)
end
);
{error, _} ->
{ok, nil}
end;
{inclusive_range, Inner@1, Low, High} ->
case extract_primitive_from_parsed(Inner@1) of
{ok, Primitive@1} ->
gleam@result:'try'(
validate_string_matches_primitive(
Low,
Primitive@1,
Context_name
),
fun(_) ->
validate_string_matches_primitive(
High,
Primitive@1,
Context_name
)
end
);
{error, _} ->
{ok, nil}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 693).
?DOC(" Checks if a parsed type is String-based (String primitive or String refinement).\n").
-spec is_string_based_parsed_type(caffeine_lang@types:parsed_type()) -> boolean().
is_string_based_parsed_type(Typ) ->
case Typ of
{parsed_primitive, string} ->
true;
{parsed_refinement, {one_of, {parsed_primitive, string}, _}} ->
true;
_ ->
false
end.
-file("src/caffeine_lang/frontend/validator.gleam", 654).
?DOC(" Validates that a Dict key type resolves to a String-based type.\n").
-spec validate_dict_key_type(
caffeine_lang@types:parsed_type(),
binary(),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_dict_key_type(Key_type, Context_name, Type_alias_map) ->
case Key_type of
{parsed_primitive, string} ->
{ok, nil};
{parsed_type_alias_ref, Alias_name} ->
case gleam@list:key_find(Type_alias_map, Alias_name) of
{ok, Resolved} ->
gleam@bool:guard(
is_string_based_parsed_type(Resolved),
{ok, nil},
fun() ->
{error,
{invalid_dict_key_type_alias,
Alias_name,
caffeine_lang@types:parsed_type_to_string(
Resolved
),
Context_name}}
end
);
{error, _} ->
{ok, nil}
end;
{parsed_refinement, {one_of, {parsed_primitive, string}, _}} ->
{ok, nil};
_ ->
{error,
{invalid_dict_key_type_alias,
<<"inline"/utf8>>,
caffeine_lang@types:parsed_type_to_string(Key_type),
Context_name}}
end.
-file("src/caffeine_lang/frontend/validator.gleam", 593).
?DOC(
" Validates that all ParsedTypeAliasRef in a type are defined.\n"
" Uses try_each_inner_parsed to handle the structural decomposition of compound types.\n"
" Adds special Dict key validation and refinement value validation.\n"
).
-spec validate_type_refs(
caffeine_lang@types:parsed_type(),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_type_refs(Typ, Context_name, Type_alias_names, Type_alias_map) ->
case Typ of
{parsed_primitive, _} ->
{ok, nil};
{parsed_type_alias_ref, Name} ->
gleam@bool:guard(
gleam@set:contains(Type_alias_names, Name),
{ok, nil},
fun() ->
{error,
{undefined_type_alias,
Name,
Context_name,
gleam@set:to_list(Type_alias_names)}}
end
);
{parsed_collection, {dict, Key, _}} ->
gleam@result:'try'(
validate_dict_key_type(Key, Context_name, Type_alias_map),
fun(_) ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner) ->
validate_type_refs(
Inner,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end
);
{parsed_refinement, Refinement} ->
gleam@result:'try'(
validate_refinement_values(Refinement, Context_name),
fun(_) ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner@1) ->
validate_type_refs(
Inner@1,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end
);
_ ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner@2) ->
validate_type_refs(
Inner@2,
Context_name,
Type_alias_names,
Type_alias_map
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 575).
?DOC(" Validates type alias references in a list of fields.\n").
-spec validate_fields_type_refs(
list(caffeine_lang@frontend@ast:field()),
binary(),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_fields_type_refs(
Fields,
Context_name,
Type_alias_names,
Type_alias_map
) ->
gleam@list:try_each(Fields, fun(Field) -> case erlang:element(3, Field) of
{type_value, Typ} ->
validate_type_refs(
Typ,
Context_name,
Type_alias_names,
Type_alias_map
);
{literal_value, _} ->
{ok, nil}
end end).
-file("src/caffeine_lang/frontend/validator.gleam", 559).
?DOC(" Validates type alias references in measurement items.\n").
-spec validate_measurement_items_type_refs(
list(caffeine_lang@frontend@ast:measurement_item()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_measurement_items_type_refs(Items, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Items,
fun(Item) ->
validate_fields_type_refs(
erlang:element(2, erlang:element(5, Item)),
erlang:element(2, Item),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 543).
?DOC(" Validates type alias references in extendables.\n").
-spec validate_extendables_type_refs(
list(caffeine_lang@frontend@ast:extendable()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_extendables_type_refs(Extendables, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Extendables,
fun(Ext) ->
validate_fields_type_refs(
erlang:element(2, erlang:element(4, Ext)),
erlang:element(2, Ext),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 532).
?DOC(" Validates type alias definitions for type refs and refinement values.\n").
-spec validate_type_aliases_type_refs(
list(caffeine_lang@frontend@ast:type_alias()),
gleam@set:set(binary()),
list({binary(), caffeine_lang@types:parsed_type()})
) -> {ok, nil} | {error, validator_error()}.
validate_type_aliases_type_refs(Type_aliases, Type_alias_names, Type_alias_map) ->
gleam@list:try_each(
Type_aliases,
fun(Ta) ->
validate_type_refs(
erlang:element(3, Ta),
erlang:element(2, Ta),
Type_alias_names,
Type_alias_map
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 826).
?DOC(" Collects errors from a list of independent validation results.\n").
-spec collect_errors(list({ok, nil} | {error, validator_error()})) -> list(validator_error()).
collect_errors(Results) ->
_pipe = Results,
gleam@list:flat_map(_pipe, fun errors_to_list/1).
-file("src/caffeine_lang/frontend/validator.gleam", 462).
?DOC(" Builds a map of type alias name to its type for validation.\n").
-spec build_type_alias_map(list(caffeine_lang@frontend@ast:type_alias())) -> list({binary(),
caffeine_lang@types:parsed_type()}).
build_type_alias_map(Type_aliases) ->
caffeine_lang@frontend@ast:build_type_alias_pairs(Type_aliases).
-file("src/caffeine_lang/frontend/validator.gleam", 256).
?DOC(" Validates that no extendable shares a name with a type alias.\n").
-spec validate_no_extendable_type_alias_collision(
list(caffeine_lang@frontend@ast:extendable()),
list(caffeine_lang@frontend@ast:type_alias())
) -> {ok, nil} | {error, validator_error()}.
validate_no_extendable_type_alias_collision(Extendables, Type_aliases) ->
Type_alias_names = begin
_pipe = Type_aliases,
_pipe@1 = gleam@list:map(_pipe, fun(Ta) -> erlang:element(2, Ta) end),
gleam@set:from_list(_pipe@1)
end,
gleam@list:try_each(
Extendables,
fun(Ext) ->
gleam@bool:guard(
gleam@set:contains(Type_alias_names, erlang:element(2, Ext)),
{error,
{extendable_type_alias_name_collision,
erlang:element(2, Ext)}},
fun() -> {ok, nil} end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 231).
-spec validate_no_duplicates_loop(
list(NGA),
fun((NGA) -> binary()),
fun((binary()) -> validator_error()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicates_loop(Items, Get_name, Make_error, Seen) ->
case Items of
[] ->
{ok, nil};
[First | Rest] ->
Name = Get_name(First),
gleam@bool:guard(
gleam@set:contains(Seen, Name),
{error, Make_error(Name)},
fun() ->
validate_no_duplicates_loop(
Rest,
Get_name,
Make_error,
gleam@set:insert(Seen, Name)
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 223).
?DOC(" Validates that no two items in a list share the same name.\n").
-spec validate_no_duplicates(
list(NFW),
fun((NFW) -> binary()),
fun((binary()) -> validator_error())
) -> {ok, nil} | {error, validator_error()}.
validate_no_duplicates(Items, Get_name, Make_error) ->
validate_no_duplicates_loop(Items, Get_name, Make_error, gleam@set:new()).
-file("src/caffeine_lang/frontend/validator.gleam", 487).
?DOC(
" Checks if a type contains a circular reference.\n"
" Uses try_each_inner_parsed to handle the structural decomposition of compound types,\n"
" with leaf-specific logic for ParsedPrimitive and ParsedTypeAliasRef.\n"
).
-spec validate_type_alias_not_circular(
binary(),
caffeine_lang@types:parsed_type(),
list({binary(), caffeine_lang@types:parsed_type()}),
list(binary()),
gleam@set:set(binary())
) -> {ok, nil} | {error, validator_error()}.
validate_type_alias_not_circular(
Original_name,
Typ,
Type_alias_map,
Visited_path,
Visited_set
) ->
case Typ of
{parsed_primitive, _} ->
{ok, nil};
{parsed_type_alias_ref, Name} ->
gleam@bool:guard(
gleam@set:contains(Visited_set, Name),
{error, {circular_type_alias, Original_name, Visited_path}},
fun() -> case gleam@list:key_find(Type_alias_map, Name) of
{ok, Resolved} ->
validate_type_alias_not_circular(
Original_name,
Resolved,
Type_alias_map,
[Name | Visited_path],
gleam@set:insert(Visited_set, Name)
);
{error, _} ->
{ok, nil}
end end
);
_ ->
caffeine_lang@types:try_each_inner_parsed(
Typ,
fun(Inner) ->
validate_type_alias_not_circular(
Original_name,
Inner,
Type_alias_map,
Visited_path,
Visited_set
)
end
)
end.
-file("src/caffeine_lang/frontend/validator.gleam", 469).
?DOC(" Validates that no type alias has circular references.\n").
-spec validate_no_circular_type_aliases(
list(caffeine_lang@frontend@ast:type_alias())
) -> {ok, nil} | {error, validator_error()}.
validate_no_circular_type_aliases(Type_aliases) ->
Type_alias_map = build_type_alias_map(Type_aliases),
gleam@list:try_each(
Type_aliases,
fun(Ta) ->
validate_type_alias_not_circular(
erlang:element(2, Ta),
erlang:element(3, Ta),
Type_alias_map,
[erlang:element(2, Ta)],
gleam@set:from_list([erlang:element(2, Ta)])
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 134).
?DOC(false).
-spec validate_measurements_file(
caffeine_lang@frontend@ast:measurements_file(caffeine_lang@frontend@ast:parsed())
) -> {ok,
caffeine_lang@frontend@ast:measurements_file(caffeine_lang@frontend@ast:validated())} |
{error, list(validator_error())}.
validate_measurements_file(File) ->
Type_aliases = erlang:element(2, File),
Extendables = erlang:element(3, File),
Items = erlang:element(4, File),
Type_alias_errors = begin
_pipe = validate_no_duplicates(
Type_aliases,
fun(Ta) -> erlang:element(2, Ta) end,
fun(Field@0) -> {duplicate_type_alias, Field@0} end
),
_pipe@1 = gleam@result:'try'(
_pipe,
fun(_) -> validate_no_circular_type_aliases(Type_aliases) end
),
errors_to_list(_pipe@1)
end,
Extendable_errors = collect_errors(
[validate_no_duplicates(
Extendables,
fun(E) -> erlang:element(2, E) end,
fun(Field@0) -> {duplicate_extendable, Field@0} end
),
validate_no_extendable_type_alias_collision(
Extendables,
Type_aliases
)]
),
Structural_errors = lists:append(Type_alias_errors, Extendable_errors),
guard_errors(
Structural_errors,
fun() ->
Type_alias_names = begin
_pipe@2 = Type_aliases,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Ta@1) -> erlang:element(2, Ta@1) end
),
gleam@set:from_list(_pipe@3)
end,
Type_alias_map = build_type_alias_map(Type_aliases),
Type_ref_errors = collect_errors(
[validate_type_aliases_type_refs(
Type_aliases,
Type_alias_names,
Type_alias_map
),
validate_extendables_type_refs(
Extendables,
Type_alias_names,
Type_alias_map
),
validate_measurement_items_type_refs(
Items,
Type_alias_names,
Type_alias_map
)]
),
Extends_errors = begin
_pipe@4 = validate_measurement_items_extends(Items, Extendables),
errors_to_list(_pipe@4)
end,
Dependent_errors = lists:append(Type_ref_errors, Extends_errors),
guard_errors(
Dependent_errors,
fun() ->
{ok,
caffeine_lang@frontend@ast:promote_measurements_file(
File
)}
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 328).
?DOC(" Validates extends references for expect items.\n").
-spec validate_expect_items_extends(
list(caffeine_lang@frontend@ast:expect_item()),
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_expect_items_extends(Items, Extendables) ->
Extendable_names = begin
_pipe = Extendables,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> erlang:element(2, E) end),
gleam@set:from_list(_pipe@1)
end,
Extendable_map = build_extendable_field_map(Extendables),
gleam@list:try_each(
Items,
fun(Item) ->
gleam@result:'try'(
validate_extends_exist(
erlang:element(3, Item),
erlang:element(2, Item),
Extendable_names
),
fun(_) ->
gleam@result:'try'(
validate_no_duplicate_extends(
erlang:element(3, Item),
erlang:element(2, Item)
),
fun(_) ->
With_fields = case erlang:element(
5,
erlang:element(5, Item)
) of
{some, Mb} ->
_pipe@2 = erlang:element(
2,
erlang:element(3, Mb)
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(F) -> erlang:element(2, F) end
),
gleam@set:from_list(_pipe@3);
none ->
gleam@set:new()
end,
validate_no_overshadowing(
erlang:element(2, Item),
erlang:element(3, Item),
[With_fields],
Extendable_map
)
end
)
end
)
end
).
-file("src/caffeine_lang/frontend/validator.gleam", 275).
?DOC(" Validates that all extendables in an expects file are Provides kind.\n").
-spec validate_extendables_are_provides(
list(caffeine_lang@frontend@ast:extendable())
) -> {ok, nil} | {error, validator_error()}.
validate_extendables_are_provides(Extendables) ->
case Extendables of
[] ->
{ok, nil};
[First | Rest] ->
case erlang:element(3, First) of
extendable_provides ->
validate_extendables_are_provides(Rest);
extendable_requires ->
{error,
{invalid_extendable_kind,
erlang:element(2, First),
<<"Provides"/utf8>>,
<<"Requires"/utf8>>}}
end
end.
-file("src/caffeine_lang/frontend/validator.gleam", 200).
?DOC(false).
-spec validate_expects_file(
caffeine_lang@frontend@ast:expects_file(caffeine_lang@frontend@ast:parsed())
) -> {ok,
caffeine_lang@frontend@ast:expects_file(caffeine_lang@frontend@ast:validated())} |
{error, list(validator_error())}.
validate_expects_file(File) ->
Extendables = erlang:element(2, File),
Items = erlang:element(3, File),
Structural_errors = collect_errors(
[validate_no_duplicates(
Extendables,
fun(E) -> erlang:element(2, E) end,
fun(Field@0) -> {duplicate_extendable, Field@0} end
),
validate_extendables_are_provides(Extendables)]
),
guard_errors(
Structural_errors,
fun() ->
Extends_errors = begin
_pipe = validate_expect_items_extends(Items, Extendables),
errors_to_list(_pipe)
end,
guard_errors(
Extends_errors,
fun() ->
{ok, caffeine_lang@frontend@ast:promote_expects_file(File)}
end
)
end
).