-module(poreader). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/poreader.gleam"). -export([is_singular/1, is_plural/1, get_id/1, get_plural_id/1, get_text/1, get_plural_text/2, get_context/1, get_comments/1, parse/1]). -export_type([parse_error/0, comment/0, message/0, token/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. ?MODULEDOC(" a parser for GNU gettext Portable Objects (.po) files\n"). -type parse_error() :: {lexer_error, integer(), integer(), binary()} | {parser_error, list(binary())}. -type comment() :: {flag, binary()} | {translator, binary()} | {extracted, binary()} | {reference, binary(), gleam@option:option(integer())} | {previous, binary()}. -type message() :: {singular, binary(), binary(), gleam@option:option(binary()), list(comment())} | {plural, binary(), binary(), gleam@dict:dict(integer(), binary()), gleam@option:option(binary()), list(comment())}. -type token() :: msg_id | msg_id_plural | msg_str | msg_ctx | {string_literal, binary()} | {comment_translator, binary()} | {comment_flag, binary()} | {comment_reference, binary()} | {comment_extracted, binary()} | {comment_previous, binary()} | newline | left_bracket | right_bracket | {number, integer()}. -file("src/poreader.gleam", 42). -spec ensure_trailing_newline(binary()) -> binary(). ensure_trailing_newline(S) -> case gleam_stdlib:string_ends_with(S, <<"\n"/utf8>>) orelse gleam_stdlib:string_ends_with( S, <<"\r\n"/utf8>> ) of true -> S; false -> <> end. -file("src/poreader.gleam", 66). -spec translate_lex_error(nibble@lexer:error()) -> parse_error(). translate_lex_error(Error) -> case Error of {no_match_found, Row, Col, Lexeme} -> {lexer_error, Row, Col, Lexeme} end. -file("src/poreader.gleam", 85). ?DOC(" returns true when the message is of type singular\n"). -spec is_singular(message()) -> boolean(). is_singular(Message) -> case Message of {singular, _, _, _, _} -> true; {plural, _, _, _, _, _} -> false end. -file("src/poreader.gleam", 94). ?DOC(" returns true when the message is of type plural\n"). -spec is_plural(message()) -> boolean(). is_plural(Message) -> case Message of {singular, _, _, _, _} -> false; {plural, _, _, _, _, _} -> true end. -file("src/poreader.gleam", 103). ?DOC(" returns the translation id of the message\n"). -spec get_id(message()) -> binary(). get_id(Message) -> case Message of {singular, Msgid, _, _, _} -> Msgid; {plural, Msgid@1, _, _, _, _} -> Msgid@1 end. -file("src/poreader.gleam", 127). ?DOC(" returns the plural id of the message\n"). -spec get_plural_id(message()) -> gleam@option:option(binary()). get_plural_id(Message) -> case Message of {singular, _, _, _, _} -> none; {plural, _, Msgid, _, _, _} -> {some, Msgid} end. -file("src/poreader.gleam", 134). -spec tuple_compare({integer(), LEL}, {integer(), LEL}) -> gleam@order:order(). tuple_compare(A, B) -> case erlang:element(1, A) =:= erlang:element(1, B) of true -> eq; false -> case erlang:element(1, A) < erlang:element(1, B) of true -> lt; false -> gt end end. -file("src/poreader.gleam", 113). ?DOC( " returns the text of the message\n" " when given a plural message, the first translation is returned\n" ). -spec get_text(message()) -> gleam@option:option(binary()). get_text(Message) -> case Message of {singular, _, Msgstr, _, _} -> {some, Msgstr}; {plural, _, _, Msgstr@1, _, _} -> _pipe = maps:to_list(Msgstr@1), _pipe@1 = gleam@list:sort(_pipe, fun tuple_compare/2), _pipe@2 = gleam@list:first(_pipe@1), _pipe@3 = gleam@result:map( _pipe@2, fun(El) -> erlang:element(2, El) end ), gleam@option:from_result(_pipe@3) end. -file("src/poreader.gleam", 148). ?DOC( " returns the plural translation of the message\n" " when given a singular message, the single translation is returned\n" ). -spec get_plural_text(message(), integer()) -> gleam@option:option(binary()). get_plural_text(Message, Idx) -> case Message of {singular, _, Msgstr, _, _} -> {some, Msgstr}; {plural, _, _, Msgstr@1, _, _} -> _pipe = gleam_stdlib:map_get(Msgstr@1, Idx), gleam@option:from_result(_pipe) end. -file("src/poreader.gleam", 157). ?DOC(" returns the context of the message\n"). -spec get_context(message()) -> gleam@option:option(binary()). get_context(Message) -> case Message of {singular, _, _, Ctx, _} -> Ctx; {plural, _, _, _, Ctx@1, _} -> Ctx@1 end. -file("src/poreader.gleam", 166). ?DOC(" returns the comments of the message\n"). -spec get_comments(message()) -> list(comment()). get_comments(Message) -> case Message of {singular, _, _, _, Comments} -> Comments; {plural, _, _, _, _, Comments@1} -> Comments@1 end. -file("src/poreader.gleam", 191). -spec to_string(token()) -> binary(). to_string(Token) -> case Token of {comment_extracted, _} -> <<"#."/utf8>>; {comment_flag, _} -> <<"#,"/utf8>>; {comment_previous, _} -> <<"#|"/utf8>>; {comment_reference, _} -> <<"#:"/utf8>>; {comment_translator, _} -> <<"# "/utf8>>; left_bracket -> <<"["/utf8>>; msg_ctx -> <<"msgctx"/utf8>>; msg_id -> <<"msgid"/utf8>>; msg_id_plural -> <<"msgid_plural"/utf8>>; msg_str -> <<"msgstr"/utf8>>; newline -> <<"\n"/utf8>>; {number, Int} -> erlang:integer_to_binary(Int); right_bracket -> <<"]"/utf8>>; {string_literal, Str} -> Str end. -file("src/poreader.gleam", 72). -spec translate_parse_error(nibble:dead_end(token(), any())) -> binary(). translate_parse_error(Dead_end) -> case erlang:element(3, Dead_end) of {bad_parser, Str} -> <<"bad parser: "/utf8, Str/binary>>; {custom, Str@1} -> <<"custom: "/utf8, Str@1/binary>>; end_of_input -> <<"Unexpected end of input"/utf8>>; {expected, Exp, Token} -> <<<<<<"Expected "/utf8, Exp/binary>>/binary, " got: "/utf8>>/binary, (to_string(Token))/binary>>; {unexpected, Token@1} -> <<"Unexpected token "/utf8, (to_string(Token@1))/binary>> end. -file("src/poreader.gleam", 210). -spec po_lexer() -> nibble@lexer:lexer(token(), nil). po_lexer() -> nibble@lexer:simple( [nibble@lexer:keyword( <<"msgid_plural"/utf8>>, <<" "/utf8>>, msg_id_plural ), nibble@lexer:keyword(<<"msgid"/utf8>>, <<" "/utf8>>, msg_id), nibble@lexer:keyword(<<"msgstr"/utf8>>, <<""/utf8>>, msg_str), nibble@lexer:keyword(<<"msgctx"/utf8>>, <<""/utf8>>, msg_ctx), nibble@lexer:token(<<"["/utf8>>, left_bracket), nibble@lexer:token(<<"]"/utf8>>, right_bracket), nibble@lexer:int(fun(Field@0) -> {number, Field@0} end), nibble@lexer:string( <<"\""/utf8>>, fun(Field@0) -> {string_literal, Field@0} end ), nibble@lexer:comment( <<"# "/utf8>>, fun(Field@0) -> {comment_translator, Field@0} end ), nibble@lexer:comment( <<"#, "/utf8>>, fun(Field@0) -> {comment_flag, Field@0} end ), nibble@lexer:comment( <<"#: "/utf8>>, fun(Field@0) -> {comment_reference, Field@0} end ), nibble@lexer:comment( <<"#. "/utf8>>, fun(Field@0) -> {comment_extracted, Field@0} end ), nibble@lexer:comment( <<"#| "/utf8>>, fun(Field@0) -> {comment_previous, Field@0} end ), nibble@lexer:token(<<"\n"/utf8>>, newline), nibble@lexer:token(<<"\r\n"/utf8>>, newline), begin _pipe = nibble@lexer:whitespace(nil), nibble@lexer:ignore(_pipe) end] ). -file("src/poreader.gleam", 239). -spec blank_lines() -> nibble:parser(list(nil), token(), any()). blank_lines() -> nibble:many1(nibble:token(newline)). -file("src/poreader.gleam", 243). -spec string_literal_parser() -> nibble:parser(binary(), token(), any()). string_literal_parser() -> nibble:take_map(<<"Expected string"/utf8>>, fun(Tok) -> case Tok of {string_literal, S} -> {some, S}; _ -> none end end). -file("src/poreader.gleam", 252). -spec multiline_string_parser() -> nibble:parser(binary(), token(), any()). multiline_string_parser() -> nibble:do( nibble:many1( (fun() -> nibble:do( string_literal_parser(), fun(Part) -> nibble:do( nibble:optional(nibble:token(newline)), fun(_) -> nibble:return(Part) end ) end ) end)() ), fun(Parts) -> nibble:return(erlang:list_to_binary(Parts)) end ). -file("src/poreader.gleam", 265). -spec msgid_parser() -> nibble:parser(binary(), token(), any()). msgid_parser() -> nibble:do( nibble:token(msg_id), fun(_) -> nibble:do( multiline_string_parser(), fun(Id) -> nibble:return(Id) end ) end ). -file("src/poreader.gleam", 271). -spec msgctx_parser() -> nibble:parser(binary(), token(), any()). msgctx_parser() -> nibble:do( nibble:token(msg_ctx), fun(_) -> nibble:do( multiline_string_parser(), fun(Ctx) -> nibble:return(Ctx) end ) end ). -file("src/poreader.gleam", 277). -spec msgid_plural_parser() -> nibble:parser(binary(), token(), any()). msgid_plural_parser() -> nibble:do( nibble:token(msg_id_plural), fun(_) -> nibble:do( multiline_string_parser(), fun(Id) -> nibble:return(Id) end ) end ). -file("src/poreader.gleam", 283). -spec msgstr_parser() -> nibble:parser(binary(), token(), any()). msgstr_parser() -> nibble:do( nibble:token(msg_str), fun(_) -> nibble:do( multiline_string_parser(), fun(Str) -> nibble:return(Str) end ) end ). -file("src/poreader.gleam", 289). -spec msgstr_plural_parser() -> nibble:parser(gleam@dict:dict(integer(), binary()), token(), any()). msgstr_plural_parser() -> _pipe = nibble:many( (fun() -> nibble:do( nibble:token(msg_str), fun(_) -> nibble:do( nibble:token(left_bracket), fun(_) -> nibble:do( nibble:take_map( <<"expected index"/utf8>>, fun(Tok) -> case Tok of {number, Idx} -> {some, Idx}; _ -> none end end ), fun(Index) -> nibble:do( nibble:token(right_bracket), fun(_) -> nibble:do( multiline_string_parser(), fun(Str) -> nibble:return({Index, Str}) end ) end ) end ) end ) end ) end)() ), nibble:map(_pipe, fun(Values) -> maps:from_list(Values) end). -file("src/poreader.gleam", 308). -spec comment_flag_parser() -> nibble:parser(list(comment()), token(), any()). comment_flag_parser() -> _pipe@1 = nibble:many1( (fun() -> nibble:do( nibble:take_map( <<"expected comment"/utf8>>, fun(Tok) -> case Tok of {comment_flag, F} -> {some, F}; _ -> none end end ), fun(Comment) -> nibble:do( nibble:token(newline), fun(_) -> Flags = begin _pipe = gleam@string:split( Comment, <<", "/utf8>> ), gleam@list:map( _pipe, fun(Str) -> {flag, Str} end ) end, nibble:return(Flags) end ) end ) end)() ), nibble:map(_pipe@1, fun lists:append/1). -file("src/poreader.gleam", 325). -spec comment_translator_parser() -> nibble:parser(list(comment()), token(), any()). comment_translator_parser() -> nibble:many1( (fun() -> nibble:do( nibble:take_map( <<"expected comment"/utf8>>, fun(Tok) -> case Tok of {comment_translator, T} -> {some, T}; _ -> none end end ), fun(Comment) -> nibble:do( nibble:token(newline), fun(_) -> nibble:return({translator, Comment}) end ) end ) end)() ). -file("src/poreader.gleam", 340). -spec comment_extracted_parser() -> nibble:parser(list(comment()), token(), any()). comment_extracted_parser() -> nibble:many1( (fun() -> nibble:do( nibble:take_map( <<"expected comment"/utf8>>, fun(Tok) -> case Tok of {comment_extracted, T} -> {some, T}; _ -> none end end ), fun(Comment) -> nibble:do( nibble:token(newline), fun(_) -> nibble:return({extracted, Comment}) end ) end ) end)() ). -file("src/poreader.gleam", 355). -spec comment_previous_parser() -> nibble:parser(list(comment()), token(), any()). comment_previous_parser() -> nibble:many1( (fun() -> nibble:do( nibble:take_map( <<"expected comment"/utf8>>, fun(Tok) -> case Tok of {comment_previous, T} -> {some, T}; _ -> none end end ), fun(Comment) -> nibble:do( nibble:token(newline), fun(_) -> nibble:return({previous, Comment}) end ) end ) end)() ). -file("src/poreader.gleam", 370). -spec comment_reference_parser() -> nibble:parser(list(comment()), token(), any()). comment_reference_parser() -> nibble:many1( (fun() -> nibble:do( nibble:take_map( <<"Expected comment"/utf8>>, fun(Tok) -> case Tok of {comment_reference, R} -> {some, R}; _ -> none end end ), fun(Comment) -> nibble:do( nibble:token(newline), fun(_) -> case gleam@string:split_once(Comment, <<":"/utf8>>) of {ok, {File, Line}} -> Number = begin _pipe = gleam_stdlib:parse_int(Line), gleam@option:from_result(_pipe) end, nibble:return({reference, File, Number}); _ -> nibble:return({reference, Comment, none}) end end ) end ) end)() ). -file("src/poreader.gleam", 392). -spec message_parser() -> nibble:parser(message(), token(), any()). message_parser() -> nibble:do( begin _pipe = nibble:many( nibble:one_of( [comment_flag_parser(), comment_reference_parser(), comment_translator_parser(), comment_extracted_parser(), comment_previous_parser()] ) ), nibble:map(_pipe, fun lists:append/1) end, fun(Comments) -> nibble:do( nibble:optional(msgctx_parser()), fun(Msgctx) -> nibble:do( msgid_parser(), fun(Msgid) -> nibble:do( nibble:optional(msgid_plural_parser()), fun(Msgid_plural) -> case Msgid_plural of none -> nibble:do( msgstr_parser(), fun(Msgstr) -> nibble:return( {singular, Msgid, Msgstr, Msgctx, Comments} ) end ); {some, Msgid_plural@1} -> nibble:do( msgstr_plural_parser(), fun(Msgstrs) -> nibble:return( {plural, Msgid, Msgid_plural@1, Msgstrs, Msgctx, Comments} ) end ) end end ) end ) end ) end ). -file("src/poreader.gleam", 422). -spec po_parser() -> nibble:parser(list(message()), token(), any()). po_parser() -> nibble:do( nibble:many(blank_lines()), fun(_) -> nibble:do( nibble:many( (fun() -> nibble:do( message_parser(), fun(Msg) -> nibble:do( nibble:many(blank_lines()), fun(_) -> nibble:return(Msg) end ) end ) end)() ), fun(Messages) -> nibble:do( nibble:many(blank_lines()), fun(_) -> nibble:do( nibble:eof(), fun(_) -> nibble:return(Messages) end ) end ) end ) end ). -file("src/poreader.gleam", 51). ?DOC(" parses the given string into a list of messages\n"). -spec parse(binary()) -> {ok, list(message())} | {error, parse_error()}. parse(Content) -> Content@1 = ensure_trailing_newline(Content), gleam@result:'try'( begin _pipe = nibble@lexer:run(Content@1, po_lexer()), gleam@result:map_error(_pipe, fun translate_lex_error/1) end, fun(Tokens) -> gleam@result:'try'( begin _pipe@1 = nibble:run(Tokens, po_parser()), gleam@result:map_error( _pipe@1, fun(Error) -> {parser_error, gleam@list:map( Error, fun translate_parse_error/1 )} end ) end, fun(Parsed) -> {ok, Parsed} end ) end ).