Current section
Files
Jump to
Current section
Files
src/yamleam@parser.erl
-module(yamleam@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yamleam/parser.gleam").
-export([parse/1, parse_all/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" YAML parser — block-style only.\n"
"\n"
" Consumes the `List(Line)` produced by the lexer and constructs a\n"
" `YamlNode` tree. Recursive descent with indent-column tracking.\n"
"\n"
" Three families of blocks:\n"
" - Mappings (lines whose first tokens are `key: ...`)\n"
" - Sequences (lines whose first token is `-`)\n"
" - Single scalars (a single line containing one scalar token)\n"
"\n"
" The interesting case is `- key: value` opening an inline mapping\n"
" inside a sequence item. The parser tracks the column where the\n"
" mapping's first key sits and treats subsequent lines at that column\n"
" as continuations of the same mapping until indent drops or a dash\n"
" reappears at the sequence's indent level.\n"
"\n"
" Anchors are threaded explicitly as a state parameter (`Anchors`)\n"
" through every parser function. A fresh table is created at the\n"
" start of each document and accumulated as `&name` declarations are\n"
" encountered; alias lookups (`*name`) read from the table at the\n"
" site of use. Nothing escapes the parser.\n"
).
-file("src/yamleam/parser.gleam", 137).
-spec finalize_segments(
list(yamleam@token:line()),
list(list(yamleam@token:line()))
) -> list(list(yamleam@token:line())).
finalize_segments(Current, Acc) ->
Acc2 = case lists:reverse(Current) of
[] ->
Acc;
Seg ->
[Seg | Acc]
end,
lists:reverse(Acc2).
-file("src/yamleam/parser.gleam", 148).
-spec segment_start_position(list(yamleam@token:line())) -> {integer(),
integer()}.
segment_start_position(Segment) ->
case Segment of
[{line, Indent, Line, _} | _] ->
{Line, Indent};
[] ->
{1, 1}
end.
-file("src/yamleam/parser.gleam", 155).
-spec is_document_marker(yamleam@token:line()) -> boolean().
is_document_marker(Line) ->
case erlang:element(4, Line) of
[{token, document_start, _}] ->
true;
[{token, document_end, _}] ->
true;
_ ->
false
end.
-file("src/yamleam/parser.gleam", 114).
-spec do_split_documents(
list(yamleam@token:line()),
list(yamleam@token:line()),
list(list(yamleam@token:line()))
) -> list(list(yamleam@token:line())).
do_split_documents(Remaining, Current, Acc) ->
case Remaining of
[] ->
finalize_segments(Current, Acc);
[Line | Rest] ->
case is_document_marker(Line) of
true ->
Acc2 = case lists:reverse(Current) of
[] ->
Acc;
Seg ->
[Seg | Acc]
end,
do_split_documents(Rest, [], Acc2);
false ->
do_split_documents(Rest, [Line | Current], Acc)
end
end.
-file("src/yamleam/parser.gleam", 110).
?DOC(
" Split a flat list of lines into per-document segments using\n"
" `DocumentStart` (`---`) and `DocumentEnd` (`...`) markers as\n"
" separators. Empty segments (e.g. consecutive markers) are dropped.\n"
).
-spec split_into_documents(list(yamleam@token:line())) -> list(list(yamleam@token:line())).
split_into_documents(Lines) ->
do_split_documents(Lines, [], []).
-file("src/yamleam/parser.gleam", 294).
?DOC(
" YAML 1.2 §3.2.2.1 requires mapping keys to be unique. We enforce this\n"
" for explicit entries only — merged entries (from `<<:`) can be silently\n"
" overridden by later explicit assignments per YAML merge semantics.\n"
).
-spec explicit_duplicate_error(binary(), list(binary()), yamleam@token:line()) -> {ok,
nil} |
{error, yamleam@error:yaml_error()}.
explicit_duplicate_error(Key, Explicit_keys, Line) ->
case gleam@list:contains(Explicit_keys, Key) of
true ->
{error,
{parse_error,
<<<<"duplicate mapping key '"/utf8, Key/binary>>/binary,
"'"/utf8>>,
erlang:element(3, Line),
erlang:element(2, Line)}};
false ->
{ok, nil}
end.
-file("src/yamleam/parser.gleam", 315).
?DOC(
" Upsert a key/value into the accumulator. If the key already exists\n"
" (from a previous merge), the value is replaced in place — preserving\n"
" the original entry's position. If the key is new, the entry is\n"
" prepended (acc is reverse-built; the prepend becomes \"appended\" after\n"
" the final list.reverse).\n"
).
-spec upsert_key(
list({binary(), yamleam@node:yaml_node()}),
binary(),
yamleam@node:yaml_node()
) -> list({binary(), yamleam@node:yaml_node()}).
upsert_key(Acc, Key, Value) ->
case gleam@list:key_find(Acc, Key) of
{ok, _} ->
gleam@list:map(
Acc,
fun(Pair) -> case erlang:element(1, Pair) =:= Key of
true ->
{Key, Value};
false ->
Pair
end end
);
{error, _} ->
[{Key, Value} | Acc]
end.
-file("src/yamleam/parser.gleam", 455).
-spec merge_key_error(binary(), yamleam@token:line()) -> yamleam@error:yaml_error().
merge_key_error(Message, Line) ->
{parse_error, Message, erlang:element(3, Line), erlang:element(2, Line)}.
-file("src/yamleam/parser.gleam", 476).
-spec merge_pairs_into_acc(
list({binary(), yamleam@node:yaml_node()}),
list({binary(), yamleam@node:yaml_node()})
) -> list({binary(), yamleam@node:yaml_node()}).
merge_pairs_into_acc(Pairs, Acc) ->
case Pairs of
[] ->
Acc;
[{K, V} | Rest] ->
case gleam@list:key_find(Acc, K) of
{ok, _} ->
merge_pairs_into_acc(Rest, Acc);
{error, _} ->
merge_pairs_into_acc(Rest, [{K, V} | Acc])
end
end.
-file("src/yamleam/parser.gleam", 459).
-spec merge_list_into_acc(
list(yamleam@node:yaml_node()),
list({binary(), yamleam@node:yaml_node()})
) -> {ok, list({binary(), yamleam@node:yaml_node()})} | {error, binary()}.
merge_list_into_acc(Items, Acc) ->
case Items of
[] ->
{ok, Acc};
[Item | Rest] ->
case Item of
{yaml_map, Pairs} ->
Next = merge_pairs_into_acc(Pairs, Acc),
merge_list_into_acc(Rest, Next);
_ ->
{error,
<<"merge key '<<' list must contain only mappings"/utf8>>}
end
end.
-file("src/yamleam/parser.gleam", 444).
?DOC(
" Merge a referenced mapping (or list of mappings) into the current\n"
" accumulator. Existing keys win over merged keys per YAML 1.1 merge\n"
" semantics. Returns Error(message) if the value isn't a mapping or\n"
" a list of mappings.\n"
).
-spec merge_into_acc(
yamleam@node:yaml_node(),
list({binary(), yamleam@node:yaml_node()})
) -> {ok, list({binary(), yamleam@node:yaml_node()})} | {error, binary()}.
merge_into_acc(Value, Acc) ->
case Value of
{yaml_map, Pairs} ->
{ok, merge_pairs_into_acc(Pairs, Acc)};
{yaml_list, Items} ->
merge_list_into_acc(Items, Acc);
_ ->
{error,
<<"merge key '<<' value must be a mapping or list of mappings"/utf8>>}
end.
-file("src/yamleam/parser.gleam", 492).
-spec extract_mapping_entry(yamleam@token:line()) -> {ok,
{binary(), list(yamleam@token:token())}} |
{error, yamleam@error:yaml_error()}.
extract_mapping_entry(Line) ->
case erlang:element(4, Line) of
[{token, {plain_scalar, K}, _}, {token, colon, _} | Rest] ->
{ok, {K, Rest}};
[{token, {quoted_scalar, K@1}, _}, {token, colon, _} | Rest@1] ->
{ok, {K@1, Rest@1}};
_ ->
{error,
{parse_error,
<<"expected mapping entry of the form 'key: value'"/utf8>>,
erlang:element(3, Line),
erlang:element(2, Line)}}
end.
-file("src/yamleam/parser.gleam", 509).
-spec is_mapping_entry(list(yamleam@token:token())) -> boolean().
is_mapping_entry(Tokens) ->
case Tokens of
[{token, {plain_scalar, _}, _}, {token, colon, _} | _] ->
true;
[{token, {quoted_scalar, _}, _}, {token, colon, _} | _] ->
true;
_ ->
false
end.
-file("src/yamleam/parser.gleam", 770).
-spec starts_with_dash(list(yamleam@token:token())) -> boolean().
starts_with_dash(Tokens) ->
case Tokens of
[{token, dash, _} | _] ->
true;
_ ->
false
end.
-file("src/yamleam/parser.gleam", 1127).
-spec scalar_token_to_node(yamleam@token:token()) -> yamleam@node:yaml_node().
scalar_token_to_node(T) ->
case erlang:element(2, T) of
{plain_scalar, Text} ->
yamleam@scalar:resolve(Text);
{quoted_scalar, Text@1} ->
{yaml_string, Text@1};
_ ->
yaml_null
end.
-file("src/yamleam/parser.gleam", 1138).
?DOC(
" Convert a scalar YamlNode into a mapping key string for implicit\n"
" flow-sequence mappings (`[key: value]`). Non-scalar keys are rejected\n"
" as complex keys, which are not supported.\n"
).
-spec scalar_flow_key_to_string(yamleam@node:yaml_node(), integer(), integer()) -> {ok,
binary()} |
{error, yamleam@error:yaml_error()}.
scalar_flow_key_to_string(Node, Line, Column) ->
case Node of
{yaml_string, S} ->
{ok, S};
{yaml_int, I} ->
{ok, erlang:integer_to_binary(I)};
{yaml_float, F} ->
{ok, gleam_stdlib:float_to_string(F)};
{yaml_bool, true} ->
{ok, <<"true"/utf8>>};
{yaml_bool, false} ->
{ok, <<"false"/utf8>>};
yaml_null ->
{ok, <<"null"/utf8>>};
_ ->
{error,
{parse_error,
<<"complex keys are not supported in flow sequence pairs"/utf8>>,
Line,
Column}}
end.
-file("src/yamleam/parser.gleam", 1159).
-spec drop_first(list(EJS)) -> list(EJS).
drop_first(Items) ->
case Items of
[] ->
[];
[_ | Rest] ->
Rest
end.
-file("src/yamleam/parser.gleam", 332).
-spec consume_mapping_value(
binary(),
list(yamleam@token:token()),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
consume_mapping_value(
Key,
Value_tokens,
First,
Rest,
Indent,
Acc,
Explicit_keys,
Anchors
) ->
case Value_tokens of
[] ->
case parse_block(Rest, Indent + 1, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After, Anchors2}} ->
add_mapping_entry(
Key,
Value,
First,
After,
Indent,
Acc,
Explicit_keys,
Anchors2
)
end;
[{token, {anchor_marker, Name}, _}] ->
case parse_block(Rest, Indent + 1, Anchors) of
{error, E@1} ->
{error, E@1};
{ok, {Value@1, After@1, Anchors2@1}} ->
Anchors3 = yamleam@anchors:set(Anchors2@1, Name, Value@1),
add_mapping_entry(
Key,
Value@1,
First,
After@1,
Indent,
Acc,
Explicit_keys,
Anchors3
)
end;
_ ->
case inline_value_to_node(
Value_tokens,
erlang:element(3, First),
Anchors
) of
{error, E@2} ->
{error, E@2};
{ok, {Value@2, Anchors2@2}} ->
add_mapping_entry(
Key,
Value@2,
First,
Rest,
Indent,
Acc,
Explicit_keys,
Anchors2@2
)
end
end.
-file("src/yamleam/parser.gleam", 167).
?DOC(
" Parse a block. The first line must be at column ≥ `min_indent`. The\n"
" block's actual indent is determined by the first line's column.\n"
).
-spec parse_block(
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_block(Lines, Min_indent, Anchors) ->
case Lines of
[] ->
{ok, {yaml_null, [], Anchors}};
[First | _] ->
case erlang:element(2, First) < Min_indent of
true ->
{ok, {yaml_null, Lines, Anchors}};
false ->
dispatch_block(Lines, First, Anchors)
end
end.
-file("src/yamleam/parser.gleam", 182).
-spec dispatch_block(
list(yamleam@token:line()),
yamleam@token:line(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
dispatch_block(Lines, First, Anchors) ->
Block_indent = erlang:element(2, First),
case erlang:element(4, First) of
[] ->
{error,
{parse_error,
<<"internal: empty token line"/utf8>>,
erlang:element(3, First),
Block_indent}};
[{token, dash, _} | _] ->
parse_sequence(Lines, Block_indent, Anchors);
[{token, {plain_scalar, _}, _}, {token, colon, _} | _] ->
parse_mapping(Lines, Block_indent, Anchors);
[{token, {quoted_scalar, _}, _}, {token, colon, _} | _] ->
parse_mapping(Lines, Block_indent, Anchors);
[{token, flow_seq_start, _} | _] ->
case parse_flow_value(
erlang:element(4, First),
erlang:element(3, First),
Anchors
) of
{error, E} ->
{error, E};
{ok, {Node, [], Anchors2}} ->
Rest = case Lines of
[_ | R] ->
R;
[] ->
[]
end,
{ok, {Node, Rest, Anchors2}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
[{token, flow_map_start, _} | _] ->
case parse_flow_value(
erlang:element(4, First),
erlang:element(3, First),
Anchors
) of
{error, E} ->
{error, E};
{ok, {Node, [], Anchors2}} ->
Rest = case Lines of
[_ | R] ->
R;
[] ->
[]
end,
{ok, {Node, Rest, Anchors2}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
[Single] ->
Value = scalar_token_to_node(Single),
Rest@1 = case Lines of
[_ | R@1] ->
R@1;
[] ->
[]
end,
{ok, {Value, Rest@1, Anchors}};
_ ->
{error,
{parse_error,
<<"unexpected token sequence; expected mapping entry, sequence item, or single scalar"/utf8>>,
erlang:element(3, First),
Block_indent}}
end.
-file("src/yamleam/parser.gleam", 238).
-spec parse_mapping(
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_mapping(Lines, Indent, Anchors) ->
do_parse_mapping(Lines, Indent, [], [], Anchors).
-file("src/yamleam/parser.gleam", 251).
?DOC(
" `acc` carries every entry in the mapping (both explicit and merged).\n"
" `explicit_keys` carries only the keys that came from explicit\n"
" assignment (not from a `<<:` merge). The duplicate-key check applies\n"
" to `explicit_keys` only — merged entries may be silently overridden\n"
" by later explicit assignments per YAML merge semantics.\n"
).
-spec do_parse_mapping(
list(yamleam@token:line()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
do_parse_mapping(Lines, Indent, Acc, Explicit_keys, Anchors) ->
case Lines of
[] ->
{ok, {{yaml_map, lists:reverse(Acc)}, [], Anchors}};
[First | Rest] ->
case (erlang:element(2, First) =:= Indent) andalso is_mapping_entry(
erlang:element(4, First)
) of
false ->
{ok, {{yaml_map, lists:reverse(Acc)}, Lines, Anchors}};
true ->
case extract_mapping_entry(First) of
{error, E} ->
{error, E};
{ok, {Key, Value_tokens}} ->
Dup_check = case Key of
<<"<<"/utf8>> ->
{ok, nil};
_ ->
explicit_duplicate_error(
Key,
Explicit_keys,
First
)
end,
case Dup_check of
{error, E@1} ->
{error, E@1};
{ok, _} ->
consume_mapping_value(
Key,
Value_tokens,
First,
Rest,
Indent,
Acc,
Explicit_keys,
Anchors
)
end
end
end
end.
-file("src/yamleam/parser.gleam", 85).
-spec parse_one_document(list(yamleam@token:line())) -> {ok,
yamleam@node:yaml_node()} |
{error, yamleam@error:yaml_error()}.
parse_one_document(Lines) ->
Anchors = yamleam@anchors:new(),
case Lines of
[] ->
{ok, yaml_null};
_ ->
case parse_block(Lines, 1, Anchors) of
{error, E} ->
{error, E};
{ok, {Node, [], _}} ->
{ok, Node};
{ok, {_, [{line, Col, Ln, _} | _], _}} ->
{error,
{parse_error,
<<"unexpected content after document, indent "/utf8,
(erlang:integer_to_binary(Col))/binary>>,
Ln,
Col}}
end
end.
-file("src/yamleam/parser.gleam", 43).
?DOC(
" Top-level entry: parse a list of lexer lines into a single `YamlNode`\n"
" tree. If the source contains multiple documents (more than one\n"
" non-empty segment separated by `---`/`...`), returns a `ParseError`.\n"
" Use `parse_all` if you need every document in a stream.\n"
).
-spec parse(list(yamleam@token:line())) -> {ok, yamleam@node:yaml_node()} |
{error, yamleam@error:yaml_error()}.
parse(Lines) ->
Segments = split_into_documents(Lines),
case Segments of
[] ->
{ok, yaml_null};
[First] ->
parse_one_document(First);
[First@1, Second | _] ->
case parse_one_document(First@1) of
{error, E} ->
{error, E};
{ok, _} ->
{Line, Column} = segment_start_position(Second),
{error,
{parse_error,
<<"multiple YAML documents found; use parse_all or parse_documents_raw for streams"/utf8>>,
Line,
Column}}
end
end.
-file("src/yamleam/parser.gleam", 71).
-spec parse_segments(
list(list(yamleam@token:line())),
list(yamleam@node:yaml_node())
) -> {ok, list(yamleam@node:yaml_node())} | {error, yamleam@error:yaml_error()}.
parse_segments(Segments, Acc) ->
case Segments of
[] ->
{ok, lists:reverse(Acc)};
[Seg | Rest] ->
case parse_one_document(Seg) of
{error, E} ->
{error, E};
{ok, Node} ->
parse_segments(Rest, [Node | Acc])
end
end.
-file("src/yamleam/parser.gleam", 66).
?DOC(
" Parse all documents in a YAML stream, splitting on `---` / `...`\n"
" markers. Returns one `YamlNode` per non-empty document. Empty\n"
" documents (between consecutive markers) are skipped.\n"
).
-spec parse_all(list(yamleam@token:line())) -> {ok,
list(yamleam@node:yaml_node())} |
{error, yamleam@error:yaml_error()}.
parse_all(Lines) ->
Segments = split_into_documents(Lines),
parse_segments(Segments, []).
-file("src/yamleam/parser.gleam", 408).
?DOC(
" Add an entry to a mapping accumulator, with special handling for the\n"
" merge key `<<`. When the key is `<<`, the value must be a `YamlMap`\n"
" (typically obtained via an alias `<<: *base`) or a list of mappings;\n"
" its entries are merged into the accumulator. Existing entries take\n"
" precedence per YAML merge-key semantics.\n"
"\n"
" For explicit (non-merge) keys: if the key already exists in `acc`\n"
" (because a previous merge added it), it's removed and replaced.\n"
).
-spec add_mapping_entry(
binary(),
yamleam@node:yaml_node(),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
add_mapping_entry(
Key,
Value,
Entry_line,
Rest,
Indent,
Acc,
Explicit_keys,
Anchors
) ->
case Key of
<<"<<"/utf8>> ->
case merge_into_acc(Value, Acc) of
{ok, Merged} ->
do_parse_mapping(
Rest,
Indent,
Merged,
Explicit_keys,
Anchors
);
{error, Msg} ->
{error, merge_key_error(Msg, Entry_line)}
end;
_ ->
do_parse_mapping(
Rest,
Indent,
upsert_key(Acc, Key, Value),
[Key | Explicit_keys],
Anchors
)
end.
-file("src/yamleam/parser.gleam", 1090).
-spec add_inline_mapping_entry(
binary(),
yamleam@node:yaml_node(),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
add_inline_mapping_entry(
Key,
Value,
Entry_line,
Rest,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors
) ->
case Key of
<<"<<"/utf8>> ->
case merge_into_acc(Value, Acc_reversed) of
{ok, Merged} ->
continue_inline_mapping(
Merged,
Explicit_keys,
Rest,
Mapping_indent,
Anchors
);
{error, Msg} ->
{error, merge_key_error(Msg, Entry_line)}
end;
_ ->
continue_inline_mapping(
upsert_key(Acc_reversed, Key, Value),
[Key | Explicit_keys],
Rest,
Mapping_indent,
Anchors
)
end.
-file("src/yamleam/parser.gleam", 985).
?DOC(
" Continue building an inline mapping started by a `- key: value` line.\n"
" Stops as soon as a line breaks the indent contract (column ≠\n"
" `mapping_indent` or not shaped like a mapping entry).\n"
).
-spec continue_inline_mapping(
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
continue_inline_mapping(
Acc_reversed,
Explicit_keys,
Lines,
Mapping_indent,
Anchors
) ->
case Lines of
[] ->
{ok, {{yaml_map, lists:reverse(Acc_reversed)}, [], Anchors}};
[First | Rest] ->
case (erlang:element(2, First) =:= Mapping_indent) andalso is_mapping_entry(
erlang:element(4, First)
) of
false ->
{ok,
{{yaml_map, lists:reverse(Acc_reversed)},
Lines,
Anchors}};
true ->
case extract_mapping_entry(First) of
{error, E} ->
{error, E};
{ok, {Key, Value_tokens}} ->
Dup_check = case Key of
<<"<<"/utf8>> ->
{ok, nil};
_ ->
explicit_duplicate_error(
Key,
Explicit_keys,
First
)
end,
case Dup_check of
{error, E@1} ->
{error, E@1};
{ok, _} ->
consume_inline_mapping_value(
Key,
Value_tokens,
First,
Rest,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors
)
end
end
end
end.
-file("src/yamleam/parser.gleam", 1026).
?DOC(" Add a value (possibly merge-keyed) to an inline mapping accumulator.\n").
-spec consume_inline_mapping_value(
binary(),
list(yamleam@token:token()),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
list(binary()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
consume_inline_mapping_value(
Key,
Value_tokens,
First,
Rest,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors
) ->
case Value_tokens of
[] ->
case parse_block(Rest, Mapping_indent + 1, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After, Anchors2}} ->
add_inline_mapping_entry(
Key,
Value,
First,
After,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors2
)
end;
[{token, {anchor_marker, Name}, _}] ->
case parse_block(Rest, Mapping_indent + 1, Anchors) of
{error, E@1} ->
{error, E@1};
{ok, {Value@1, After@1, Anchors2@1}} ->
Anchors3 = yamleam@anchors:set(Anchors2@1, Name, Value@1),
add_inline_mapping_entry(
Key,
Value@1,
First,
After@1,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors3
)
end;
_ ->
case inline_value_to_node(
Value_tokens,
erlang:element(3, First),
Anchors
) of
{error, E@2} ->
{error, E@2};
{ok, {Value@2, Anchors2@2}} ->
add_inline_mapping_entry(
Key,
Value@2,
First,
Rest,
Mapping_indent,
Acc_reversed,
Explicit_keys,
Anchors2@2
)
end
end.
-file("src/yamleam/parser.gleam", 955).
?DOC(
" Bootstrap an inline mapping accumulator from the first key/value pair,\n"
" handling the merge-key special case.\n"
).
-spec start_inline_mapping(
binary(),
yamleam@node:yaml_node(),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
start_inline_mapping(Key, Value, Entry_line, Rest, Mapping_indent, Anchors) ->
case Key of
<<"<<"/utf8>> ->
case merge_into_acc(Value, []) of
{ok, Merged} ->
continue_inline_mapping(
Merged,
[],
Rest,
Mapping_indent,
Anchors
);
{error, Msg} ->
{error, merge_key_error(Msg, Entry_line)}
end;
_ ->
continue_inline_mapping(
[{Key, Value}],
[Key],
Rest,
Mapping_indent,
Anchors
)
end.
-file("src/yamleam/parser.gleam", 628).
-spec parse_flow_seq(
list(yamleam@token:token()),
integer(),
list(yamleam@node:yaml_node()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:token()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_flow_seq(Tokens, Line_num, Acc, Anchors) ->
case Tokens of
[] ->
{error,
{parse_error,
<<"unterminated flow sequence: expected ']'"/utf8>>,
Line_num,
1}};
[First | Rest] ->
case erlang:element(2, First) of
flow_seq_end ->
{ok, {{yaml_list, lists:reverse(Acc)}, Rest, Anchors}};
comma ->
parse_flow_seq(Rest, Line_num, Acc, Anchors);
_ ->
case parse_flow_value(Tokens, Line_num, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After, Anchors2}} ->
case After of
[Colon_tok | After_colon] when erlang:element(
2,
Colon_tok
) =:= colon ->
case scalar_flow_key_to_string(
Value,
erlang:element(
2,
erlang:element(3, Colon_tok)
),
erlang:element(
3,
erlang:element(3, Colon_tok)
)
) of
{error, E@1} ->
{error, E@1};
{ok, Key_str} ->
case parse_flow_value(
After_colon,
Line_num,
Anchors2
) of
{error, E@2} ->
{error, E@2};
{ok,
{Map_value,
After_value,
Anchors3}} ->
Map_node = {yaml_map,
[{Key_str, Map_value}]},
parse_flow_seq(
After_value,
Line_num,
[Map_node | Acc],
Anchors3
)
end
end;
_ ->
parse_flow_seq(
After,
Line_num,
[Value | Acc],
Anchors2
)
end
end
end
end.
-file("src/yamleam/parser.gleam", 581).
-spec parse_flow_value(
list(yamleam@token:token()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:token()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_flow_value(Tokens, Line_num, Anchors) ->
case Tokens of
[] ->
{error, {parse_error, <<"expected flow value"/utf8>>, Line_num, 1}};
[First | Rest] ->
case erlang:element(2, First) of
flow_seq_start ->
parse_flow_seq(
Rest,
erlang:element(2, erlang:element(3, First)),
[],
Anchors
);
flow_map_start ->
parse_flow_map(
Rest,
erlang:element(2, erlang:element(3, First)),
[],
Anchors
);
{plain_scalar, _} ->
{ok, {scalar_token_to_node(First), Rest, Anchors}};
{quoted_scalar, _} ->
{ok, {scalar_token_to_node(First), Rest, Anchors}};
{anchor_marker, Name} ->
case parse_flow_value(Rest, Line_num, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After, Anchors2}} ->
Anchors3 = yamleam@anchors:set(
Anchors2,
Name,
Value
),
{ok, {Value, After, Anchors3}}
end;
{alias_marker, Name@1} ->
case yamleam@anchors:get(Anchors, Name@1) of
{ok, Value@1} ->
{ok, {Value@1, Rest, Anchors}};
{error, _} ->
{error,
{parse_error,
<<<<"unknown alias '*"/utf8, Name@1/binary>>/binary,
"'"/utf8>>,
erlang:element(2, erlang:element(3, First)),
erlang:element(3, erlang:element(3, First))}}
end;
_ ->
{error,
{parse_error,
<<"unexpected token in flow value"/utf8>>,
erlang:element(2, erlang:element(3, First)),
erlang:element(3, erlang:element(3, First))}}
end
end.
-file("src/yamleam/parser.gleam", 517).
-spec inline_value_to_node(
list(yamleam@token:token()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
inline_value_to_node(Tokens, Line_num, Anchors) ->
case Tokens of
[] ->
{ok, {yaml_null, Anchors}};
[{token, {anchor_marker, Name}, _} | Rest] ->
case inline_value_to_node(Rest, Line_num, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, Anchors2}} ->
Anchors3 = yamleam@anchors:set(Anchors2, Name, Value),
{ok, {Value, Anchors3}}
end;
[{token, {alias_marker, Name@1}, Pos}] ->
case yamleam@anchors:get(Anchors, Name@1) of
{ok, Value@1} ->
{ok, {Value@1, Anchors}};
{error, _} ->
{error,
{parse_error,
<<<<"unknown alias '*"/utf8, Name@1/binary>>/binary,
"'"/utf8>>,
erlang:element(2, Pos),
erlang:element(3, Pos)}}
end;
[Single] ->
{ok, {scalar_token_to_node(Single), Anchors}};
[First | _] ->
case erlang:element(2, First) of
flow_seq_start ->
case parse_flow_value(Tokens, Line_num, Anchors) of
{error, E@1} ->
{error, E@1};
{ok, {Node, [], Anchors2@1}} ->
{ok, {Node, Anchors2@1}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
flow_map_start ->
case parse_flow_value(Tokens, Line_num, Anchors) of
{error, E@1} ->
{error, E@1};
{ok, {Node, [], Anchors2@1}} ->
{ok, {Node, Anchors2@1}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
_ ->
{error,
{parse_error,
<<"value contains multiple tokens; quote the value if it includes ':' followed by space"/utf8>>,
Line_num,
1}}
end
end.
-file("src/yamleam/parser.gleam", 865).
?DOC(" Handle `- &name <value>` and `- &name` followed by a nested block.\n").
-spec parse_sequence_item_after_anchor(
binary(),
list(yamleam@token:token()),
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_sequence_item_after_anchor(
Name,
Rest_content,
Dash_line,
Rest_lines,
Seq_indent,
Anchors
) ->
case Rest_content of
[] ->
case parse_block(Rest_lines, Seq_indent + 1, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After, Anchors2}} ->
Anchors3 = yamleam@anchors:set(Anchors2, Name, Value),
{ok, {Value, After, Anchors3}}
end;
_ ->
case inline_value_to_node(
Rest_content,
erlang:element(3, Dash_line),
Anchors
) of
{error, E@1} ->
{error, E@1};
{ok, {Value@1, Anchors2@1}} ->
Anchors3@1 = yamleam@anchors:set(Anchors2@1, Name, Value@1),
{ok, {Value@1, Rest_lines, Anchors3@1}}
end
end.
-file("src/yamleam/parser.gleam", 903).
?DOC(
" `- key: value` (or `- key:` with nested value) opens a mapping whose\n"
" effective indent column is the column where the first key sits. This\n"
" function builds the first entry, then continues consuming subsequent\n"
" mapping entries from `rest_lines` at the same effective indent.\n"
).
-spec parse_inline_mapping_item(
binary(),
yamleam@token:token(),
list(yamleam@token:token()),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_inline_mapping_item(Key, First_tok, Value_tokens, Rest_lines, Anchors) ->
Mapping_indent = erlang:element(3, erlang:element(3, First_tok)),
case Value_tokens of
[] ->
case parse_block(Rest_lines, Mapping_indent + 1, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After_value, Anchors2}} ->
start_inline_mapping(
Key,
Value,
{line,
Mapping_indent,
erlang:element(2, erlang:element(3, First_tok)),
[]},
After_value,
Mapping_indent,
Anchors2
)
end;
_ ->
case inline_value_to_node(
Value_tokens,
erlang:element(2, erlang:element(3, First_tok)),
Anchors
) of
{error, E@1} ->
{error, E@1};
{ok, {Value@1, Anchors2@1}} ->
start_inline_mapping(
Key,
Value@1,
{line,
Mapping_indent,
erlang:element(2, erlang:element(3, First_tok)),
[]},
Rest_lines,
Mapping_indent,
Anchors2@1
)
end
end.
-file("src/yamleam/parser.gleam", 777).
-spec parse_sequence_item(
yamleam@token:line(),
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_sequence_item(Dash_line, Rest, Seq_indent, Anchors) ->
Content = drop_first(erlang:element(4, Dash_line)),
case Content of
[] ->
parse_block(Rest, Seq_indent + 1, Anchors);
[{token, {anchor_marker, Name}, _} | Rest_content] ->
parse_sequence_item_after_anchor(
Name,
Rest_content,
Dash_line,
Rest,
Seq_indent,
Anchors
);
[{token, {alias_marker, Name@1}, Pos}] ->
case yamleam@anchors:get(Anchors, Name@1) of
{ok, Value} ->
{ok, {Value, Rest, Anchors}};
{error, _} ->
{error,
{parse_error,
<<<<"unknown alias '*"/utf8, Name@1/binary>>/binary,
"'"/utf8>>,
erlang:element(2, Pos),
erlang:element(3, Pos)}}
end;
[{token, flow_seq_start, _} | _] ->
case parse_flow_value(
Content,
erlang:element(3, Dash_line),
Anchors
) of
{error, E} ->
{error, E};
{ok, {Node, [], Anchors2}} ->
{ok, {Node, Rest, Anchors2}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
[{token, flow_map_start, _} | _] ->
case parse_flow_value(
Content,
erlang:element(3, Dash_line),
Anchors
) of
{error, E} ->
{error, E};
{ok, {Node, [], Anchors2}} ->
{ok, {Node, Rest, Anchors2}};
{ok, {_, [Trail | _], _}} ->
{error,
{parse_error,
<<"unexpected tokens after flow collection"/utf8>>,
erlang:element(2, erlang:element(3, Trail)),
erlang:element(3, erlang:element(3, Trail))}}
end;
[Single] ->
case erlang:element(2, Single) of
{plain_scalar, _} ->
{ok, {scalar_token_to_node(Single), Rest, Anchors}};
{quoted_scalar, _} ->
{ok, {scalar_token_to_node(Single), Rest, Anchors}};
_ ->
{error,
{parse_error,
<<"unexpected token after '-'"/utf8>>,
erlang:element(3, Dash_line),
erlang:element(2, Dash_line)}}
end;
[First_tok, {token, colon, _} | Value_tokens] ->
case erlang:element(2, First_tok) of
{plain_scalar, K} ->
parse_inline_mapping_item(
K,
First_tok,
Value_tokens,
Rest,
Anchors
);
{quoted_scalar, K@1} ->
parse_inline_mapping_item(
K@1,
First_tok,
Value_tokens,
Rest,
Anchors
);
_ ->
{error,
{parse_error,
<<"expected key after '-'"/utf8>>,
erlang:element(3, Dash_line),
erlang:element(2, Dash_line)}}
end;
_ ->
{error,
{parse_error,
<<"unexpected token sequence after '-'"/utf8>>,
erlang:element(3, Dash_line),
erlang:element(2, Dash_line)}}
end.
-file("src/yamleam/parser.gleam", 749).
-spec do_parse_sequence(
list(yamleam@token:line()),
integer(),
list(yamleam@node:yaml_node()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
do_parse_sequence(Lines, Indent, Acc, Anchors) ->
case Lines of
[] ->
{ok, {{yaml_list, lists:reverse(Acc)}, [], Anchors}};
[First | Rest] ->
case (erlang:element(2, First) =:= Indent) andalso starts_with_dash(
erlang:element(4, First)
) of
false ->
{ok, {{yaml_list, lists:reverse(Acc)}, Lines, Anchors}};
true ->
case parse_sequence_item(First, Rest, Indent, Anchors) of
{error, E} ->
{error, E};
{ok, {Item, After, Anchors2}} ->
do_parse_sequence(
After,
Indent,
[Item | Acc],
Anchors2
)
end
end
end.
-file("src/yamleam/parser.gleam", 741).
-spec parse_sequence(
list(yamleam@token:line()),
integer(),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:line()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_sequence(Lines, Indent, Anchors) ->
do_parse_sequence(Lines, Indent, [], Anchors).
-file("src/yamleam/parser.gleam", 702).
-spec parse_flow_map_entry(
binary(),
list(yamleam@token:token()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:token()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_flow_map_entry(Key, After_key, Line_num, Acc, Anchors) ->
case After_key of
[{token, colon, _} | After_colon] ->
case parse_flow_value(After_colon, Line_num, Anchors) of
{error, E} ->
{error, E};
{ok, {Value, After_value, Anchors2}} ->
case gleam@list:key_find(Acc, Key) of
{ok, _} ->
{error,
{parse_error,
<<<<"duplicate mapping key '"/utf8,
Key/binary>>/binary,
"' in flow mapping"/utf8>>,
Line_num,
1}};
{error, _} ->
parse_flow_map(
After_value,
Line_num,
[{Key, Value} | Acc],
Anchors2
)
end
end;
_ ->
{error,
{parse_error,
<<<<"expected ':' after key '"/utf8, Key/binary>>/binary,
"' in flow mapping"/utf8>>,
Line_num,
1}}
end.
-file("src/yamleam/parser.gleam", 673).
-spec parse_flow_map(
list(yamleam@token:token()),
integer(),
list({binary(), yamleam@node:yaml_node()}),
gleam@dict:dict(binary(), yamleam@node:yaml_node())
) -> {ok,
{yamleam@node:yaml_node(),
list(yamleam@token:token()),
gleam@dict:dict(binary(), yamleam@node:yaml_node())}} |
{error, yamleam@error:yaml_error()}.
parse_flow_map(Tokens, Line_num, Acc, Anchors) ->
case Tokens of
[] ->
{error,
{parse_error,
<<"unterminated flow mapping: expected '}'"/utf8>>,
Line_num,
1}};
[First | Rest] ->
case erlang:element(2, First) of
flow_map_end ->
{ok, {{yaml_map, lists:reverse(Acc)}, Rest, Anchors}};
comma ->
parse_flow_map(Rest, Line_num, Acc, Anchors);
{plain_scalar, K} ->
parse_flow_map_entry(K, Rest, Line_num, Acc, Anchors);
{quoted_scalar, K@1} ->
parse_flow_map_entry(K@1, Rest, Line_num, Acc, Anchors);
_ ->
{error,
{parse_error,
<<"expected key in flow mapping"/utf8>>,
erlang:element(2, erlang:element(3, First)),
erlang:element(3, erlang:element(3, First))}}
end
end.