From 68e0be174146abcb883b2123122791d95ed741f3 Mon Sep 17 00:00:00 2001 From: Mikko Ahlroth Date: Thu, 9 May 2024 18:49:58 +0300 Subject: [PATCH] Initial commit (WIP WIP WIP) --- .gitignore | 4 + README.md | 25 + expo_plural_forms_parser.erl | 1098 +++++++++++++++++++++++++ fi.mo | Bin 0 -> 561 bytes fi.po | 34 + gleam.toml | 21 + manifest.toml | 14 + messages.po | 22 + messages.pot | 30 + src/kielet.gleam | 20 + src/kielet/database.gleam | 49 ++ src/kielet/language.gleam | 52 ++ src/kielet/mo.gleam | 253 ++++++ src/kielet/plurals/LICENSE.txt | 13 + src/kielet/plurals/parser.notgleam | 148 ++++ src/kielet/plurals/syntax_error.gleam | 6 + src/kielet/plurals/tokenizer.gleam | 140 ++++ src/kielet/tester.gleam | 44 + test/kielet_test.gleam | 12 + test/mo_test.gleam | 8 + 20 files changed, 1993 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 expo_plural_forms_parser.erl create mode 100644 fi.mo create mode 100644 fi.po create mode 100644 gleam.toml create mode 100644 manifest.toml create mode 100644 messages.po create mode 100644 messages.pot create mode 100644 src/kielet.gleam create mode 100644 src/kielet/database.gleam create mode 100644 src/kielet/language.gleam create mode 100644 src/kielet/mo.gleam create mode 100644 src/kielet/plurals/LICENSE.txt create mode 100644 src/kielet/plurals/parser.notgleam create mode 100644 src/kielet/plurals/syntax_error.gleam create mode 100644 src/kielet/plurals/tokenizer.gleam create mode 100644 src/kielet/tester.gleam create mode 100644 test/kielet_test.gleam create mode 100644 test/mo_test.gleam diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..599be4e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +/build +erl_crash.dump diff --git a/README.md b/README.md new file mode 100644 index 0000000..4764f2a --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# kielet + +[![Package Version](https://img.shields.io/hexpm/v/kielet)](https://hex.pm/packages/kielet) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/kielet/) + +```sh +gleam add kielet +``` +```gleam +import kielet + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +gleam shell # Run an Erlang shell +``` diff --git a/expo_plural_forms_parser.erl b/expo_plural_forms_parser.erl new file mode 100644 index 0000000..b141e15 --- /dev/null +++ b/expo_plural_forms_parser.erl @@ -0,0 +1,1098 @@ +-module(expo_plural_forms_parser). +-export([parse/1, parse_and_scan/1, format_error/1]). +-file("expo_plural_forms_parser.yrl", 48). + +value({int, _Line, Int}) -> Int. + +operator({Op, _Line}) -> Op. + +-file("/Users/nicd/.asdf/installs/erlang/26.1.2/lib/parsetools-2.5/include/yeccpre.hrl", 0). +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2021. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% The parser generator will insert appropriate declarations before this line.% + +-type yecc_ret() :: {'error', _} | {'ok', _}. + +-spec parse(Tokens :: list()) -> yecc_ret(). +parse(Tokens) -> + yeccpars0(Tokens, {no_func, no_location}, 0, [], []). + +-spec parse_and_scan({function() | {atom(), atom()}, [_]} + | {atom(), atom(), [_]}) -> yecc_ret(). +parse_and_scan({F, A}) -> + yeccpars0([], {{F, A}, no_location}, 0, [], []); +parse_and_scan({M, F, A}) -> + Arity = length(A), + yeccpars0([], {{fun M:F/Arity, A}, no_location}, 0, [], []). + +-spec format_error(any()) -> [char() | list()]. +format_error(Message) -> + case io_lib:deep_char_list(Message) of + true -> + Message; + _ -> + io_lib:write(Message) + end. + +%% To be used in grammar files to throw an error message to the parser +%% toplevel. Doesn't have to be exported! +-compile({nowarn_unused_function, return_error/2}). +-spec return_error(erl_anno:location(), any()) -> no_return(). +return_error(Location, Message) -> + throw({error, {Location, ?MODULE, Message}}). + +-define(CODE_VERSION, "1.4"). + +yeccpars0(Tokens, Tzr, State, States, Vstack) -> + try yeccpars1(Tokens, Tzr, State, States, Vstack) + catch + error: Error: Stacktrace -> + try yecc_error_type(Error, Stacktrace) of + Desc -> + erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc}, + Stacktrace) + catch _:_ -> erlang:raise(error, Error, Stacktrace) + end; + %% Probably thrown from return_error/2: + throw: {error, {_Location, ?MODULE, _M}} = Error -> + Error + end. + +yecc_error_type(function_clause, [{?MODULE,F,ArityOrArgs,_} | _]) -> + case atom_to_list(F) of + "yeccgoto_" ++ SymbolL -> + {ok,[{atom,_,Symbol}],_} = erl_scan:string(SymbolL), + State = case ArityOrArgs of + [S,_,_,_,_,_,_] -> S; + _ -> state_is_unknown + end, + {Symbol, State, missing_in_goto_table} + end. + +yeccpars1([Token | Tokens], Tzr, State, States, Vstack) -> + yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens, Tzr); +yeccpars1([], {{F, A},_Location}, State, States, Vstack) -> + case apply(F, A) of + {ok, Tokens, EndLocation} -> + yeccpars1(Tokens, {{F, A}, EndLocation}, State, States, Vstack); + {eof, EndLocation} -> + yeccpars1([], {no_func, EndLocation}, State, States, Vstack); + {error, Descriptor, _EndLocation} -> + {error, Descriptor} + end; +yeccpars1([], {no_func, no_location}, State, States, Vstack) -> + Line = 999999, + yeccpars2(State, '$end', States, Vstack, yecc_end(Line), [], + {no_func, Line}); +yeccpars1([], {no_func, EndLocation}, State, States, Vstack) -> + yeccpars2(State, '$end', States, Vstack, yecc_end(EndLocation), [], + {no_func, EndLocation}). + +%% yeccpars1/7 is called from generated code. +%% +%% When using the {includefile, Includefile} option, make sure that +%% yeccpars1/7 can be found by parsing the file without following +%% include directives. yecc will otherwise assume that an old +%% yeccpre.hrl is included (one which defines yeccpars1/5). +yeccpars1(State1, State, States, Vstack, Token0, [Token | Tokens], Tzr) -> + yeccpars2(State, element(1, Token), [State1 | States], + [Token0 | Vstack], Token, Tokens, Tzr); +yeccpars1(State1, State, States, Vstack, Token0, [], {{_F,_A}, _Location}=Tzr) -> + yeccpars1([], Tzr, State, [State1 | States], [Token0 | Vstack]); +yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, no_location}) -> + Location = yecctoken_end_location(Token0), + yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack], + yecc_end(Location), [], {no_func, Location}); +yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, Location}) -> + yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack], + yecc_end(Location), [], {no_func, Location}). + +%% For internal use only. +yecc_end(Location) -> + {'$end', Location}. + +yecctoken_end_location(Token) -> + try erl_anno:end_location(element(2, Token)) of + undefined -> yecctoken_location(Token); + Loc -> Loc + catch _:_ -> yecctoken_location(Token) + end. + +-compile({nowarn_unused_function, yeccerror/1}). +yeccerror(Token) -> + Text = yecctoken_to_string(Token), + Location = yecctoken_location(Token), + {error, {Location, ?MODULE, ["syntax error before: ", Text]}}. + +-compile({nowarn_unused_function, yecctoken_to_string/1}). +yecctoken_to_string(Token) -> + try erl_scan:text(Token) of + undefined -> yecctoken2string(Token); + Txt -> Txt + catch _:_ -> yecctoken2string(Token) + end. + +yecctoken_location(Token) -> + try erl_scan:location(Token) + catch _:_ -> element(2, Token) + end. + +-compile({nowarn_unused_function, yecctoken2string/1}). +yecctoken2string(Token) -> + try + yecctoken2string1(Token) + catch + _:_ -> + io_lib:format("~tp", [Token]) + end. + +-compile({nowarn_unused_function, yecctoken2string1/1}). +yecctoken2string1({atom, _, A}) -> io_lib:write_atom(A); +yecctoken2string1({integer,_,N}) -> io_lib:write(N); +yecctoken2string1({float,_,F}) -> io_lib:write(F); +yecctoken2string1({char,_,C}) -> io_lib:write_char(C); +yecctoken2string1({var,_,V}) -> io_lib:format("~s", [V]); +yecctoken2string1({string,_,S}) -> io_lib:write_string(S); +yecctoken2string1({reserved_symbol, _, A}) -> io_lib:write(A); +yecctoken2string1({_Cat, _, Val}) -> io_lib:format("~tp", [Val]); +yecctoken2string1({dot, _}) -> "'.'"; +yecctoken2string1({'$end', _}) -> []; +yecctoken2string1({Other, _}) when is_atom(Other) -> + io_lib:write_atom(Other); +yecctoken2string1(Other) -> + io_lib:format("~tp", [Other]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +-file("expo_plural_forms_parser.erl", 188). + +-dialyzer({nowarn_function, yeccpars2/7}). +-compile({nowarn_unused_function, yeccpars2/7}). +yeccpars2(0=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_0(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(1=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_1(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(2=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_2(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(3=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_3(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(4=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_4(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(5=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_5(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(6=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_6(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(7=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_7(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(8=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_8(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(9=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_9(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(10=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_10(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(11=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_11(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(12=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(13=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_13(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(14=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_14(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(15=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_15(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(16=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_16(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(17=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_17(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(18=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_18(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(19=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(20=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_20(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(21=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_21(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(22=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(23=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_23(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(24=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_24(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(25=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_25(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(26=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_26(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(27=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(28=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_28(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(29=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(30=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(31=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_31(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(32=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_32(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(33=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_33(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(34=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_34(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(35=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_35(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(36=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_36(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(37=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_37(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(38=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_38(S, Cat, Ss, Stack, T, Ts, Tzr); +%% yeccpars2(39=S, Cat, Ss, Stack, T, Ts, Tzr) -> +%% yeccpars2_39(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(40=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_40(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(41=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_41(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(42=S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_42(S, Cat, Ss, Stack, T, Ts, Tzr); +yeccpars2(Other, _, _, _, _, _, _) -> + erlang:error({yecc_bug,"1.4",{missing_state_in_action_table, Other}}). + +-dialyzer({nowarn_function, yeccpars2_0/7}). +-compile({nowarn_unused_function, yeccpars2_0/7}). +yeccpars2_0(S, 'nplurals', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 2, Ss, Stack, T, Ts, Tzr); +yeccpars2_0(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_1/7}). +-compile({nowarn_unused_function, yeccpars2_1/7}). +yeccpars2_1(_S, '$end', _Ss, Stack, _T, _Ts, _Tzr) -> + {ok, hd(Stack)}; +yeccpars2_1(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_2/7}). +-compile({nowarn_unused_function, yeccpars2_2/7}). +yeccpars2_2(S, '=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 3, Ss, Stack, T, Ts, Tzr); +yeccpars2_2(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_3/7}). +-compile({nowarn_unused_function, yeccpars2_3/7}). +yeccpars2_3(S, 'int', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 4, Ss, Stack, T, Ts, Tzr); +yeccpars2_3(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_4/7}). +-compile({nowarn_unused_function, yeccpars2_4/7}). +yeccpars2_4(S, ';', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 5, Ss, Stack, T, Ts, Tzr); +yeccpars2_4(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_5/7}). +-compile({nowarn_unused_function, yeccpars2_5/7}). +yeccpars2_5(S, 'plural', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 6, Ss, Stack, T, Ts, Tzr); +yeccpars2_5(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_6/7}). +-compile({nowarn_unused_function, yeccpars2_6/7}). +yeccpars2_6(S, '=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 7, Ss, Stack, T, Ts, Tzr); +yeccpars2_6(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_7/7}). +-compile({nowarn_unused_function, yeccpars2_7/7}). +yeccpars2_7(S, '(', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 12, Ss, Stack, T, Ts, Tzr); +yeccpars2_7(S, 'int', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 13, Ss, Stack, T, Ts, Tzr); +yeccpars2_7(S, 'n', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 14, Ss, Stack, T, Ts, Tzr); +yeccpars2_7(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_8/7}). +-compile({nowarn_unused_function, yeccpars2_8/7}). +yeccpars2_8(S, ';', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 42, Ss, Stack, T, Ts, Tzr); +yeccpars2_8(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_9/7}). +-compile({nowarn_unused_function, yeccpars2_9/7}). +yeccpars2_9(_S, '!=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_!='(Stack), + yeccgoto_expr(hd(Ss), '!=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '%', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_%'(Stack), + yeccgoto_expr(hd(Ss), '%', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '<', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_<'(Stack), + yeccgoto_expr(hd(Ss), '<', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '<=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_<='(Stack), + yeccgoto_expr(hd(Ss), '<=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '==', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_=='(Stack), + yeccgoto_expr(hd(Ss), '==', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '>', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_>'(Stack), + yeccgoto_expr(hd(Ss), '>', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, '>=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_9_>='(Stack), + yeccgoto_expr(hd(Ss), '>=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_9(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_9_(Stack), + yeccgoto_plural_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_10/7}). +-compile({nowarn_unused_function, yeccpars2_10/7}). +yeccpars2_10(S, '!=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '%', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '<', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 31, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '<=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 32, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '==', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 33, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '>', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 34, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(S, '>=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 35, Ss, Stack, T, Ts, Tzr); +yeccpars2_10(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_11/7}). +-compile({nowarn_unused_function, yeccpars2_11/7}). +yeccpars2_11(S, '&&', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 20, Ss, Stack, T, Ts, Tzr); +yeccpars2_11(S, '?', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 22, Ss, Stack, T, Ts, Tzr); +yeccpars2_11(S, '||', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 23, Ss, Stack, T, Ts, Tzr); +yeccpars2_11(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_11_(Stack), + yeccgoto_plural_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_12/7}). +-compile({nowarn_unused_function, yeccpars2_12/7}). +yeccpars2_12(S, '(', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 12, Ss, Stack, T, Ts, Tzr); +yeccpars2_12(S, 'int', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 18, Ss, Stack, T, Ts, Tzr); +yeccpars2_12(S, 'n', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 14, Ss, Stack, T, Ts, Tzr); +yeccpars2_12(_, _, _, _, T, _, _) -> + yeccerror(T). + +-dialyzer({nowarn_function, yeccpars2_13/7}). +-compile({nowarn_unused_function, yeccpars2_13/7}). +yeccpars2_13(_S, '!=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_!='(Stack), + yeccgoto_expr(hd(Ss), '!=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '%', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_%'(Stack), + yeccgoto_expr(hd(Ss), '%', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '<', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_<'(Stack), + yeccgoto_expr(hd(Ss), '<', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '<=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_<='(Stack), + yeccgoto_expr(hd(Ss), '<=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '==', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_=='(Stack), + yeccgoto_expr(hd(Ss), '==', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '>', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_>'(Stack), + yeccgoto_expr(hd(Ss), '>', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, '>=', Ss, Stack, T, Ts, Tzr) -> + NewStack = 'yeccpars2_13_>='(Stack), + yeccgoto_expr(hd(Ss), '>=', Ss, NewStack, T, Ts, Tzr); +yeccpars2_13(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_13_(Stack), + yeccgoto_plural_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_14/7}). +-compile({nowarn_unused_function, yeccpars2_14/7}). +yeccpars2_14(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_14_(Stack), + yeccgoto_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_15/7}). +-compile({nowarn_unused_function, yeccpars2_15/7}). +yeccpars2_15(S, ')', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 41, Ss, Stack, T, Ts, Tzr); +yeccpars2_15(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_15_(Stack), + yeccgoto_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +yeccpars2_16(S, ')', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 40, Ss, Stack, T, Ts, Tzr); +yeccpars2_16(S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_10(S, Cat, Ss, Stack, T, Ts, Tzr). + +yeccpars2_17(S, ')', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 21, Ss, Stack, T, Ts, Tzr); +yeccpars2_17(S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_26(S, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_18/7}). +-compile({nowarn_unused_function, yeccpars2_18/7}). +yeccpars2_18(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_18_(Stack), + yeccgoto_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +%% yeccpars2_19: see yeccpars2_12 + +-dialyzer({nowarn_function, yeccpars2_20/7}). +-compile({nowarn_unused_function, yeccpars2_20/7}). +yeccpars2_20(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_20_(Stack), + yeccgoto_bool_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_21/7}). +-compile({nowarn_unused_function, yeccpars2_21/7}). +yeccpars2_21(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_21_(Stack), + yeccgoto_bool_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +%% yeccpars2_22: see yeccpars2_12 + +-dialyzer({nowarn_function, yeccpars2_23/7}). +-compile({nowarn_unused_function, yeccpars2_23/7}). +yeccpars2_23(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_23_(Stack), + yeccgoto_bool_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_24/7}). +-compile({nowarn_unused_function, yeccpars2_24/7}). +yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_24_(Stack), + yeccgoto_expr(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +yeccpars2_25(S, ':', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 30, Ss, Stack, T, Ts, Tzr); +yeccpars2_25(S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_10(S, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_26/7}). +-compile({nowarn_unused_function, yeccpars2_26/7}). +yeccpars2_26(S, '&&', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 20, Ss, Stack, T, Ts, Tzr); +yeccpars2_26(S, '?', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 22, Ss, Stack, T, Ts, Tzr); +yeccpars2_26(S, '||', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 23, Ss, Stack, T, Ts, Tzr); +yeccpars2_26(_, _, _, _, T, _, _) -> + yeccerror(T). + +%% yeccpars2_27: see yeccpars2_12 + +-dialyzer({nowarn_function, yeccpars2_28/7}). +-compile({nowarn_unused_function, yeccpars2_28/7}). +yeccpars2_28(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_28_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +%% yeccpars2_29: see yeccpars2_12 + +%% yeccpars2_30: see yeccpars2_12 + +-dialyzer({nowarn_function, yeccpars2_31/7}). +-compile({nowarn_unused_function, yeccpars2_31/7}). +yeccpars2_31(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_31_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_32/7}). +-compile({nowarn_unused_function, yeccpars2_32/7}). +yeccpars2_32(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_32_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_33/7}). +-compile({nowarn_unused_function, yeccpars2_33/7}). +yeccpars2_33(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_33_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_34/7}). +-compile({nowarn_unused_function, yeccpars2_34/7}). +yeccpars2_34(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_34_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_35/7}). +-compile({nowarn_unused_function, yeccpars2_35/7}). +yeccpars2_35(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + NewStack = yeccpars2_35_(Stack), + yeccgoto_comp_op(hd(Ss), Cat, Ss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_36/7}). +-compile({nowarn_unused_function, yeccpars2_36/7}). +yeccpars2_36(S, '!=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '%', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '<', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 31, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '<=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 32, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '==', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 33, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '>', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 34, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(S, '>=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 35, Ss, Stack, T, Ts, Tzr); +yeccpars2_36(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_,_,_|Nss] = Ss, + NewStack = yeccpars2_36_(Stack), + yeccgoto_if_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_37/7}). +-compile({nowarn_unused_function, yeccpars2_37/7}). +yeccpars2_37(S, '%', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr); +yeccpars2_37(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_37_(Stack), + yeccgoto_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_38/7}). +-compile({nowarn_unused_function, yeccpars2_38/7}). +yeccpars2_38(S, '!=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '%', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '<', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 31, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '<=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 32, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '==', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 33, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '>', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 34, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(S, '>=', Ss, Stack, T, Ts, Tzr) -> + yeccpars1(S, 35, Ss, Stack, T, Ts, Tzr); +yeccpars2_38(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_38_(Stack), + yeccgoto_bool_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_39/7}). +-compile({nowarn_unused_function, yeccpars2_39/7}). +yeccpars2_39(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_39_(Stack), + yeccgoto_bool_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_40/7}). +-compile({nowarn_unused_function, yeccpars2_40/7}). +yeccpars2_40(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_40_(Stack), + yeccgoto_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_41/7}). +-compile({nowarn_unused_function, yeccpars2_41/7}). +yeccpars2_41(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_|Nss] = Ss, + NewStack = yeccpars2_41_(Stack), + yeccgoto_if_expr(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccpars2_42/7}). +-compile({nowarn_unused_function, yeccpars2_42/7}). +yeccpars2_42(_S, Cat, Ss, Stack, T, Ts, Tzr) -> + [_,_,_,_,_,_,_|Nss] = Ss, + NewStack = yeccpars2_42_(Stack), + yeccgoto_plural_form(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_bool_expr/7}). +-compile({nowarn_unused_function, yeccgoto_bool_expr/7}). +yeccgoto_bool_expr(7, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_11(11, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(12, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_17(17, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(19=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_39(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(22, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(27, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(29, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_expr(30, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_bool_op/7}). +-compile({nowarn_unused_function, yeccgoto_bool_op/7}). +yeccgoto_bool_op(11, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(19, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_op(17, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(19, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_op(26, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(19, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_bool_op(39, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(19, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_comp_op/7}). +-compile({nowarn_unused_function, yeccgoto_comp_op/7}). +yeccgoto_comp_op(10, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_comp_op(16, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_comp_op(25, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_comp_op(36, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_comp_op(37, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_comp_op(38, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_12(27, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_expr/7}). +-compile({nowarn_unused_function, yeccgoto_expr/7}). +yeccgoto_expr(7, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_10(10, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(12, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_16(16, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(19, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_10(10, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(22, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_25(25, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(27, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_38(38, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(29, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_37(37, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_expr(30, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_36(36, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_if_expr/7}). +-compile({nowarn_unused_function, yeccgoto_if_expr/7}). +yeccgoto_if_expr(7=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_9(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(12, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_15(15, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(19=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(22=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(27=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(29=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr); +yeccgoto_if_expr(30=_S, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_24(_S, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_plural_expr/7}). +-compile({nowarn_unused_function, yeccgoto_plural_expr/7}). +yeccgoto_plural_expr(7, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_8(8, Cat, Ss, Stack, T, Ts, Tzr). + +-dialyzer({nowarn_function, yeccgoto_plural_form/7}). +-compile({nowarn_unused_function, yeccgoto_plural_form/7}). +yeccgoto_plural_form(0, Cat, Ss, Stack, T, Ts, Tzr) -> + yeccpars2_1(1, Cat, Ss, Stack, T, Ts, Tzr). + +-compile({inline,'yeccpars2_9_!='/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_!='/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_!='/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_!='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_%'/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_%'/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_%'/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_%'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_<'/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_<'/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_<'/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_<'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_<='/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_<='/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_<='/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_<='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_=='/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_=='/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_=='/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_=='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_>'/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_>'/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_>'/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_>'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_9_>='/1}). +-dialyzer({nowarn_function, 'yeccpars2_9_>='/1}). +-compile({nowarn_unused_function, 'yeccpars2_9_>='/1}). +-file("expo_plural_forms_parser.yrl", 30). +'yeccpars2_9_>='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,yeccpars2_9_/1}). +-dialyzer({nowarn_function, yeccpars2_9_/1}). +-compile({nowarn_unused_function, yeccpars2_9_/1}). +-file("expo_plural_forms_parser.yrl", 19). +yeccpars2_9_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,yeccpars2_11_/1}). +-dialyzer({nowarn_function, yeccpars2_11_/1}). +-compile({nowarn_unused_function, yeccpars2_11_/1}). +-file("expo_plural_forms_parser.yrl", 18). +yeccpars2_11_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,'yeccpars2_13_!='/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_!='/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_!='/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_!='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_%'/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_%'/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_%'/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_%'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_<'/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_<'/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_<'/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_<'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_<='/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_<='/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_<='/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_<='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_=='/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_=='/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_=='/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_=='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_>'/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_>'/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_>'/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_>'(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,'yeccpars2_13_>='/1}). +-dialyzer({nowarn_function, 'yeccpars2_13_>='/1}). +-compile({nowarn_unused_function, 'yeccpars2_13_>='/1}). +-file("expo_plural_forms_parser.yrl", 29). +'yeccpars2_13_>='(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,yeccpars2_13_/1}). +-dialyzer({nowarn_function, yeccpars2_13_/1}). +-compile({nowarn_unused_function, yeccpars2_13_/1}). +-file("expo_plural_forms_parser.yrl", 17). +yeccpars2_13_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,yeccpars2_14_/1}). +-dialyzer({nowarn_function, yeccpars2_14_/1}). +-compile({nowarn_unused_function, yeccpars2_14_/1}). +-file("expo_plural_forms_parser.yrl", 31). +yeccpars2_14_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + n + end | __Stack]. + +-compile({inline,yeccpars2_15_/1}). +-dialyzer({nowarn_function, yeccpars2_15_/1}). +-compile({nowarn_unused_function, yeccpars2_15_/1}). +-file("expo_plural_forms_parser.yrl", 30). +yeccpars2_15_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,yeccpars2_18_/1}). +-dialyzer({nowarn_function, yeccpars2_18_/1}). +-compile({nowarn_unused_function, yeccpars2_18_/1}). +-file("expo_plural_forms_parser.yrl", 29). +yeccpars2_18_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + value(___1) + end | __Stack]. + +-compile({inline,yeccpars2_20_/1}). +-dialyzer({nowarn_function, yeccpars2_20_/1}). +-compile({nowarn_unused_function, yeccpars2_20_/1}). +-file("expo_plural_forms_parser.yrl", 41). +yeccpars2_20_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_21_/1}). +-dialyzer({nowarn_function, yeccpars2_21_/1}). +-compile({nowarn_unused_function, yeccpars2_21_/1}). +-file("expo_plural_forms_parser.yrl", 23). +yeccpars2_21_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + {paren, ___2} + end | __Stack]. + +-compile({inline,yeccpars2_23_/1}). +-dialyzer({nowarn_function, yeccpars2_23_/1}). +-compile({nowarn_unused_function, yeccpars2_23_/1}). +-file("expo_plural_forms_parser.yrl", 42). +yeccpars2_23_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_24_/1}). +-dialyzer({nowarn_function, yeccpars2_24_/1}). +-compile({nowarn_unused_function, yeccpars2_24_/1}). +-file("expo_plural_forms_parser.yrl", 30). +yeccpars2_24_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + ___1 + end | __Stack]. + +-compile({inline,yeccpars2_28_/1}). +-dialyzer({nowarn_function, yeccpars2_28_/1}). +-compile({nowarn_unused_function, yeccpars2_28_/1}). +-file("expo_plural_forms_parser.yrl", 34). +yeccpars2_28_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_31_/1}). +-dialyzer({nowarn_function, yeccpars2_31_/1}). +-compile({nowarn_unused_function, yeccpars2_31_/1}). +-file("expo_plural_forms_parser.yrl", 37). +yeccpars2_31_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_32_/1}). +-dialyzer({nowarn_function, yeccpars2_32_/1}). +-compile({nowarn_unused_function, yeccpars2_32_/1}). +-file("expo_plural_forms_parser.yrl", 39). +yeccpars2_32_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_33_/1}). +-dialyzer({nowarn_function, yeccpars2_33_/1}). +-compile({nowarn_unused_function, yeccpars2_33_/1}). +-file("expo_plural_forms_parser.yrl", 36). +yeccpars2_33_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_34_/1}). +-dialyzer({nowarn_function, yeccpars2_34_/1}). +-compile({nowarn_unused_function, yeccpars2_34_/1}). +-file("expo_plural_forms_parser.yrl", 35). +yeccpars2_34_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_35_/1}). +-dialyzer({nowarn_function, yeccpars2_35_/1}). +-compile({nowarn_unused_function, yeccpars2_35_/1}). +-file("expo_plural_forms_parser.yrl", 38). +yeccpars2_35_(__Stack0) -> + [___1 | __Stack] = __Stack0, + [begin + operator(___1) + end | __Stack]. + +-compile({inline,yeccpars2_36_/1}). +-dialyzer({nowarn_function, yeccpars2_36_/1}). +-compile({nowarn_unused_function, yeccpars2_36_/1}). +-file("expo_plural_forms_parser.yrl", 25). +yeccpars2_36_(__Stack0) -> + [___5,___4,___3,___2,___1 | __Stack] = __Stack0, + [begin + {'if', ___1, ___3, ___5} + end | __Stack]. + +-compile({inline,yeccpars2_37_/1}). +-dialyzer({nowarn_function, yeccpars2_37_/1}). +-compile({nowarn_unused_function, yeccpars2_37_/1}). +-file("expo_plural_forms_parser.yrl", 28). +yeccpars2_37_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + {'%', ___1, ___3} + end | __Stack]. + +-compile({inline,yeccpars2_38_/1}). +-dialyzer({nowarn_function, yeccpars2_38_/1}). +-compile({nowarn_unused_function, yeccpars2_38_/1}). +-file("expo_plural_forms_parser.yrl", 21). +yeccpars2_38_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + {___2, ___1, ___3} + end | __Stack]. + +-compile({inline,yeccpars2_39_/1}). +-dialyzer({nowarn_function, yeccpars2_39_/1}). +-compile({nowarn_unused_function, yeccpars2_39_/1}). +-file("expo_plural_forms_parser.yrl", 22). +yeccpars2_39_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + {___2, ___1, ___3} + end | __Stack]. + +-compile({inline,yeccpars2_40_/1}). +-dialyzer({nowarn_function, yeccpars2_40_/1}). +-compile({nowarn_unused_function, yeccpars2_40_/1}). +-file("expo_plural_forms_parser.yrl", 32). +yeccpars2_40_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + ___2 + end | __Stack]. + +-compile({inline,yeccpars2_41_/1}). +-dialyzer({nowarn_function, yeccpars2_41_/1}). +-compile({nowarn_unused_function, yeccpars2_41_/1}). +-file("expo_plural_forms_parser.yrl", 26). +yeccpars2_41_(__Stack0) -> + [___3,___2,___1 | __Stack] = __Stack0, + [begin + {paren, ___2} + end | __Stack]. + +-compile({inline,yeccpars2_42_/1}). +-dialyzer({nowarn_function, yeccpars2_42_/1}). +-compile({nowarn_unused_function, yeccpars2_42_/1}). +-file("expo_plural_forms_parser.yrl", 15). +yeccpars2_42_(__Stack0) -> + [___8,___7,___6,___5,___4,___3,___2,___1 | __Stack] = __Stack0, + [begin + {value(___3), ___7} + end | __Stack]. + + +-file("expo_plural_forms_parser.yrl", 53). diff --git a/fi.mo b/fi.mo new file mode 100644 index 0000000000000000000000000000000000000000..995853056d45bed3629fa006404dff6291db39b0 GIT binary patch literal 561 zcmaix%}T>S5P;*~B{#*Rhw&gNvN27g)YO8gtrS`eC5lI<$(pWBcf;`;4@KOVplXp1c4uf%s*!SDb&KKesbv7ec$b& zu`JOjO@UTvYS2TOPci2#;W+u`M?hu!-F|yHOwIG%OC>FrRO6+jCYC?jqq0CDn~;uB z8*W>-NryF-=c-S{IBYq85e;~0e|H-2B1^v^bEC2ui literal 0 HcmV?d00001 diff --git a/fi.po b/fi.po new file mode 100644 index 0000000..56f94d0 --- /dev/null +++ b/fi.po @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: mikko@ahlroth.fi\n" +"POT-Creation-Date: 2024-05-09 14:33+0300\n" +"PO-Revision-Date: 2024-05-09 14:36+0300\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.4.2\n" + +#: src/kielet/tester.gleam:6 +msgid "Press to activate" +msgstr "Paina aktivoidaksesi" + +#: src/kielet/tester.gleam:9 +#, c-format +msgid "Press to activate %s button" +msgid_plural "Press to activate %s buttons" +msgstr[0] "Paina aktivoidaksesi %s nappi" +msgstr[1] "Paina aktivoidaksesi %s nappia" + +#~ msgid "foo" +#~ msgstr "Juu" diff --git a/gleam.toml b/gleam.toml new file mode 100644 index 0000000..548e219 --- /dev/null +++ b/gleam.toml @@ -0,0 +1,21 @@ +name = "kielet" +version = "1.0.0" +target = "javascript" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.34.0 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" +simplifile = ">= 1.7.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..25ff755 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,14 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, + { name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" }, + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, + { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, +] + +[requirements] +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } +simplifile = { version = ">= 1.7.0 and < 2.0.0" } diff --git a/messages.po b/messages.po new file mode 100644 index 0000000..7aacf80 --- /dev/null +++ b/messages.po @@ -0,0 +1,22 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-05-01 09:06+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/kielet/tester.gleam:5 +msgid "foo" +msgstr "" diff --git a/messages.pot b/messages.pot new file mode 100644 index 0000000..7655c26 --- /dev/null +++ b/messages.pot @@ -0,0 +1,30 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Mikko Ahlroth +# This file is distributed under the same license as the Kielet package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Kielet 1.2.3\n" +"Report-Msgid-Bugs-To: mikko@ahlroth.fi\n" +"POT-Creation-Date: 2024-05-09 18:44+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: src/kielet/tester.gleam:15 +msgid "Press to activate" +msgstr "" + +#: src/kielet/tester.gleam:18 +#, c-format +msgid "Press to activate %s button" +msgid_plural "Press to activate %s buttons" +msgstr[0] "" +msgstr[1] "" diff --git a/src/kielet.gleam b/src/kielet.gleam new file mode 100644 index 0000000..ea59ec6 --- /dev/null +++ b/src/kielet.gleam @@ -0,0 +1,20 @@ +import gleam/io +import kielet/database.{type Database} + +pub fn main() { + io.println("Hello from kielet!") +} + +pub fn gettext(db: Database, msgid: String, language_code: String) -> String { + database.translate_singular(db, msgid, language_code) +} + +pub fn ngettext( + db: Database, + singular: String, + plural: String, + count: Int, + language_code: String, +) -> String { + database.translate_plural(db, singular, plural, count, language_code) +} diff --git a/src/kielet/database.gleam b/src/kielet/database.gleam new file mode 100644 index 0000000..986f938 --- /dev/null +++ b/src/kielet/database.gleam @@ -0,0 +1,49 @@ +import gleam/dict.{type Dict} +import kielet/language.{type Language} + +pub opaque type Database { + Database(languages: Dict(String, Language)) +} + +pub fn new() { + Database(languages: dict.new()) +} + +pub fn add_language(db: Database, lang: Language) { + Database(languages: dict.insert(db.languages, language.get_code(lang), lang)) +} + +pub fn translate_singular(db: Database, msgid: String, language_code: String) { + case dict.get(db.languages, language_code) { + Ok(lang) -> + case language.get_singular_translation(lang, msgid) { + Ok(translation) -> translation + Error(_) -> msgid + } + Error(_) -> msgid + } +} + +pub fn translate_plural( + db: Database, + msgid: String, + plural: String, + count: Int, + language_code: String, +) { + let translations = case dict.get(db.languages, language_code) { + Ok(lang) -> + case language.get_plural_translations(lang, msgid) { + Ok(translations) -> translations + Error(_) -> [msgid, plural] + } + Error(_) -> [msgid, plural] + } + + let assert [singular, plural] = translations + + case count { + 1 -> singular + _ -> plural + } +} diff --git a/src/kielet/language.gleam b/src/kielet/language.gleam new file mode 100644 index 0000000..ff795b2 --- /dev/null +++ b/src/kielet/language.gleam @@ -0,0 +1,52 @@ +import gleam/dict +import gleam/result +import kielet/mo + +pub type LanguageError { + MoParseError(err: mo.ParseError) +} + +pub type TranslateError { + MsgIsSingular(String) + MsgIsPlural(String) + MsgNotFound(String) +} + +pub opaque type Language { + Language(code: String, translations: mo.Translations) +} + +pub fn load(code: String, mo_file: BitArray) { + use mo <- result.try( + mo.parse(mo_file) + |> result.map_error(MoParseError), + ) + + Ok(Language(code, mo.translations)) +} + +pub fn get_code(lang: Language) { + lang.code +} + +pub fn get_singular_translation(lang: Language, msgid: String) { + case dict.get(lang.translations, msgid) { + Ok(mostring) -> + case mostring { + mo.Singular(content: c, ..) -> Ok(c) + _ -> Error(MsgIsPlural(msgid)) + } + _ -> Error(MsgNotFound(msgid)) + } +} + +pub fn get_plural_translations(lang: Language, msgid: String) { + case dict.get(lang.translations, msgid) { + Ok(mostring) -> + case mostring { + mo.Plural(content: c, ..) -> Ok(c) + _ -> Error(MsgIsSingular(msgid)) + } + _ -> Error(MsgNotFound(msgid)) + } +} diff --git a/src/kielet/mo.gleam b/src/kielet/mo.gleam new file mode 100644 index 0000000..0730a56 --- /dev/null +++ b/src/kielet/mo.gleam @@ -0,0 +1,253 @@ +import gleam/bit_array +import gleam/bool +import gleam/dict.{type Dict} +import gleam/int +import gleam/list +import gleam/result +import gleam/string + +const max_supported_major = 0 + +const eot = "" + +const nul = "\u{0}" + +pub type MoString { + Singular(context: String, content: String) + Plural(context: String, content: List(String)) +} + +pub type Translations = + Dict(String, MoString) + +pub type Endianness { + BigEndian + LittleEndian +} + +pub type ParseError { + MagicNumberNotFound + MalformedHeader + UnknownRevision(Revision) + OffsetPastEnd(Int) + MalformedOffsetTableEntry(BitArray) + StringNotUTF8(BitArray) +} + +pub type Revision { + Revision(major: Int, minor: Int) +} + +pub type Header { + Header( + revision: Revision, + string_count: Int, + og_table_offset: Int, + trans_table_offset: Int, + ht_size: Int, + ht_offset: Int, + ) +} + +pub type Mo { + Mo(endianness: Endianness, header: Header, translations: Translations) +} + +pub fn parse(mo: BitArray) { + use #(endianness, rest) <- result.try(parse_magic(mo)) + use header <- result.try(parse_header(endianness, rest)) + use <- bool.guard( + header.revision.major > max_supported_major, + Error(UnknownRevision(header.revision)), + ) + use <- bool.guard( + header.string_count == 0, + Ok(Mo(endianness: endianness, header: header, translations: dict.new())), + ) + + let total_size = bit_array.byte_size(mo) + + use <- bool.guard( + header.og_table_offset >= total_size, + Error(OffsetPastEnd(header.og_table_offset)), + ) + use <- bool.guard( + header.trans_table_offset >= total_size, + Error(OffsetPastEnd(header.trans_table_offset)), + ) + use <- bool.guard( + header.ht_offset >= total_size, + Error(OffsetPastEnd(header.ht_offset)), + ) + + use translations <- result.try(parse_translations(endianness, header, mo)) + + Ok(Mo(endianness: endianness, header: header, translations: translations)) +} + +fn parse_magic(body: BitArray) { + case body { + <<0xde120495:size(32), rest:bytes>> -> Ok(#(LittleEndian, rest)) + <<0x950412de:size(32), rest:bytes>> -> Ok(#(BigEndian, rest)) + _ -> Error(MagicNumberNotFound) + } +} + +fn parse_header(endianness: Endianness, body: BitArray) { + case endianness { + LittleEndian -> { + case body { + << + major_bytes:bytes-size(2), + minor_bytes:bytes-size(2), + string_count_bytes:bytes-size(4), + og_table_offset_bytes:bytes-size(4), + trans_table_offset_bytes:bytes-size(4), + ht_size_bytes:bytes-size(4), + ht_offset_bytes:bytes-size(4), + _rest:bytes, + >> -> { + let assert Ok(major) = le_int_8(major_bytes) + let assert Ok(minor) = le_int_8(minor_bytes) + let assert Ok(string_count) = le_int_32(string_count_bytes) + let assert Ok(og_table_offset) = le_int_32(og_table_offset_bytes) + let assert Ok(trans_table_offset) = + le_int_32(trans_table_offset_bytes) + let assert Ok(ht_size) = le_int_32(ht_size_bytes) + let assert Ok(ht_offset) = le_int_32(ht_offset_bytes) + Ok(Header( + Revision(major, minor), + string_count, + og_table_offset, + trans_table_offset, + ht_size, + ht_offset, + )) + } + _ -> Error(MalformedHeader) + } + } + + BigEndian -> panic + } +} + +fn parse_translations(endianness: Endianness, header: Header, mo: BitArray) { + let strings = list.range(0, header.string_count - 1) + use translations <- result.try( + list.try_fold(strings, dict.new(), fn(translations, i) { + let new_offset = i * 8 + let og_offset = header.og_table_offset + new_offset + let trans_offset = header.trans_table_offset + new_offset + use #(og, translation) <- result.try(parse_translation( + endianness, + mo, + og_offset, + trans_offset, + )) + + let key = case og { + Singular(content: c, ..) -> c + Plural(content: [c, ..], ..) -> c + Plural(..) -> panic as "Got plural form with zero entries" + } + + Ok(dict.insert(translations, key, translation)) + }), + ) + + Ok(translations) +} + +fn parse_translation( + endianness: Endianness, + mo: BitArray, + og_offset: Int, + trans_offset: Int, +) { + use #(og_str_length, og_str_offset) <- result.try(parse_offset_table_entry( + endianness, + mo, + og_offset, + )) + use #(trans_str_length, trans_str_offset) <- result.try( + parse_offset_table_entry(endianness, mo, trans_offset), + ) + + use og_string <- result.try(parse_mo_string(mo, og_str_length, og_str_offset)) + use trans_string <- result.try(parse_mo_string( + mo, + trans_str_length, + trans_str_offset, + )) + + Ok(#(og_string, trans_string)) +} + +fn parse_offset_table_entry(endianness: Endianness, mo: BitArray, offset: Int) { + use data <- result.try( + bit_array.slice(mo, offset, 8) + |> result.replace_error(OffsetPastEnd(offset)), + ) + case endianness { + LittleEndian -> + case data { + <> -> { + let assert Ok(target_length) = le_int_32(target_length) + let assert Ok(target_offset) = le_int_32(target_offset) + Ok(#(target_length, target_offset)) + } + _ -> Error(MalformedOffsetTableEntry(data)) + } + + BigEndian -> panic + } +} + +fn parse_mo_string(mo: BitArray, length: Int, offset: Int) { + use data <- result.try( + bit_array.slice(mo, offset, length) + |> result.replace_error(OffsetPastEnd(offset)), + ) + + use str <- result.try( + bit_array.to_string(data) + |> result.replace_error(StringNotUTF8(data)), + ) + + let #(context, str) = case string.split_once(str, eot) { + Ok(#(c, s)) -> #(c, s) + Error(_) -> #("", str) + } + + case string.split(str, nul) { + [_] -> Ok(Singular(context: context, content: str)) + plurals -> Ok(Plural(context: context, content: plurals)) + } +} + +fn le_int_8(int8: BitArray) { + case int8 { + <> -> Ok(reconstruct_ui8(h, l)) + _ -> Error(Nil) + } +} + +fn le_int_32(int32: BitArray) { + case int32 { + <> -> + Ok(reconstruct_ui32(hh, hl, lh, ll)) + _ -> Error(Nil) + } +} + +fn reconstruct_ui8(h: Int, l: Int) { + int.bitwise_shift_left(h, 8) + l +} + +fn reconstruct_ui32(hh: Int, hl: Int, lh: Int, ll: Int) { + int.bitwise_shift_left(hh, 8 * 3) + + int.bitwise_shift_left(hl, 8 * 2) + + int.bitwise_shift_left(lh, 8) + + ll +} diff --git a/src/kielet/plurals/LICENSE.txt b/src/kielet/plurals/LICENSE.txt new file mode 100644 index 0000000..69bcec1 --- /dev/null +++ b/src/kielet/plurals/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2015 Plataformatec Copyright 2020 Dashbit 2022 JOSHMARTIN GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/kielet/plurals/parser.notgleam b/src/kielet/plurals/parser.notgleam new file mode 100644 index 0000000..f2c129f --- /dev/null +++ b/src/kielet/plurals/parser.notgleam @@ -0,0 +1,148 @@ +import gleam/result +import kielet/plurals/syntax_error +import kielet/plurals/tokenizer + +pub type ParseError { + SyntaxError(err: syntax_error.SyntaxError) +} + +pub type PluralForms { + PluralForms(nplurals: Int, ast: Ast) +} + +type BinOp { + Equal + NotEqual + GreaterThan + GreaterThanOrEqual + LowerThan + LowerThanOrEqual + Remainder + And + Or +} + +pub opaque type Ast { + N + Integer(Int) + BinaryOperation(operator: BinOp, lvalue: Ast, rvalue: Ast) + If(condition: Ast, truthy: Ast, falsy: Ast) + Paren(Ast) +} + +pub fn parse(rules: String) { + use tokens <- result.try( + tokenizer.tokenize(rules) + |> result.map_error(SyntaxError), + ) + + do_parse(tokens) +} + +fn eval_ast(ast: Ast, input: Int) { + case ast { + N -> input + Integer(i) -> i + If(condition, truthy, falsy) -> { + let ast = case eval_ast(condition, input) { + 1 -> truthy + _ -> falsy + } + eval_ast(ast, input) + } + Paren(content) -> eval_ast(content, input) + BinaryOperation(operator, lvalue, rvalue) -> { + let lvalue = eval_ast(lvalue, input) + let rvalue = eval_ast(rvalue, input) + case operator { + Equal -> bool_to_int(lvalue == rvalue) + NotEqual -> bool_to_int(lvalue != rvalue) + GreaterThan -> bool_to_int(lvalue > rvalue) + GreaterThanOrEqual -> bool_to_int(lvalue >= rvalue) + LowerThan -> bool_to_int(lvalue < rvalue) + LowerThanOrEqual -> bool_to_int(lvalue <= rvalue) + Remainder -> lvalue % rvalue + And -> bool_to_int(lvalue == 1 && rvalue == 1) + Or -> bool_to_int(lvalue == 1 || rvalue == 1) + } + } + } +} + +fn bool_to_int(bool: Bool) { + case bool { + True -> 1 + False -> 0 + } +} + +type Location { + NoLocation + Line(Int) +} + +fn do_parse(tokens: List(tokenizer.Token)) { + yeccpars0(tokens, NoLocation, 0, [], []) +} + +fn yeccpars0( + tokens: List(tokenizer.Token), + location: Location, + state: Int, + states: List(Int), + vstack: List(tokenizer.Token), +) { + yeccpars1(tokens, location, state, states, vstack) +} + +fn yeccpars1( + tokens: List(tokenizer.Token), + location: Location, + state: Int, + states: List(Int), + vstack: List(tokenizer.Token), +) { + case tokens { + [token, ..rest] -> + yeccpars2(state, token, states, vstack, token, rest, location) + [] -> + case location { + NoLocation -> + yeccpars2( + state, + tokenizer.End, + states, + vstack, + #(tokenizer.End, 999_999), + [], + 999_999, + ) + Line(line) -> + yeccpars2( + state, + tokenizer.End, + states, + vstac, + #(tokenizer.End, line), + [], + line, + ) + } + } +} + +fn yeccpars1_2(state1, state, states, vstack, token0, tokens, location) { + case tokens { + [token, ..rest] -> + yeccpars2( + state, + token, + [state1, ..states], + [token0, ..vstack], + token, + rest, + location, + ) + [] -> yeccpars2() + } +} diff --git a/src/kielet/plurals/syntax_error.gleam b/src/kielet/plurals/syntax_error.gleam new file mode 100644 index 0000000..e02f0e5 --- /dev/null +++ b/src/kielet/plurals/syntax_error.gleam @@ -0,0 +1,6 @@ +import gleam/option + +/// Error returned when the syntax in a plural forms string is invalid. +pub type SyntaxError { + SyntaxError(line: Int, column: option.Option(Int), reason: String) +} diff --git a/src/kielet/plurals/tokenizer.gleam b/src/kielet/plurals/tokenizer.gleam new file mode 100644 index 0000000..59e09da --- /dev/null +++ b/src/kielet/plurals/tokenizer.gleam @@ -0,0 +1,140 @@ +import gleam/int +import gleam/list +import gleam/option +import gleam/string +import kielet/plurals/syntax_error.{SyntaxError} + +pub type Token { + Token(type_: TokenType, line: Int) + Int(value: Int, line: Int) +} + +pub type TokenType { + N + NPlurals + Plural + Equals + NotEquals + GreaterThanOrEquals + LowerThanOrEquals + GreaterThan + LowerThan + Assignment + Ternary + TernaryElse + Remainder + Or + And + Semicolon + LParen + RParen + End +} + +pub fn tokenize(str: String) { + do_tokenize(string.to_graphemes(str), [], 1, 1) +} + +fn do_tokenize(str: List(String), acc: List(Token), line: Int, col: Int) { + case str { + [] -> Ok(list.reverse([Token(End, line), ..acc])) + ["n", "p", "l", "u", "r", "a", "l", "s", ..rest] -> + do_tokenize(rest, [Token(NPlurals, line), ..acc], line, col + 8) + ["p", "l", "u", "r", "a", "l", ..rest] -> + do_tokenize(rest, [Token(Plural, line), ..acc], line, col + 6) + ["n", ..rest] -> do_tokenize(rest, [Token(N, line), ..acc], line, col + 1) + ["\\", "\n", ..rest] -> do_tokenize(rest, acc, line + 1, 1) + ["\n", ..rest] -> do_tokenize(rest, acc, line + 1, 1) + [" ", ..rest] -> do_tokenize(rest, acc, line, col + 1) + ["=", "=", ..rest] -> + do_tokenize(rest, [Token(Equals, line), ..acc], line, col + 2) + ["!", "=", ..rest] -> + do_tokenize(rest, [Token(NotEquals, line), ..acc], line, col + 2) + [">", "=", ..rest] -> + do_tokenize( + rest, + [Token(GreaterThanOrEquals, line), ..acc], + line, + col + 2, + ) + ["<", "=", ..rest] -> + do_tokenize(rest, [Token(LowerThanOrEquals, line), ..acc], line, col + 2) + [">", ..rest] -> + do_tokenize(rest, [Token(GreaterThan, line), ..acc], line, col + 1) + ["<", ..rest] -> + do_tokenize(rest, [Token(LowerThan, line), ..acc], line, col + 1) + ["=", ..rest] -> + do_tokenize(rest, [Token(Assignment, line), ..acc], line, col + 1) + ["?", ..rest] -> + do_tokenize(rest, [Token(Ternary, line), ..acc], line, col + 1) + [":", ..rest] -> + do_tokenize(rest, [Token(TernaryElse, line), ..acc], line, col + 1) + ["%", ..rest] -> + do_tokenize(rest, [Token(Remainder, line), ..acc], line, col + 1) + ["|", "|", ..rest] -> + do_tokenize(rest, [Token(Or, line), ..acc], line, col + 2) + ["&", "&", ..rest] -> + do_tokenize(rest, [Token(And, line), ..acc], line, col + 2) + [";", ..rest] -> + do_tokenize(rest, [Token(Semicolon, line), ..acc], line, col + 1) + [")", ..rest] -> + do_tokenize(rest, [Token(RParen, line), ..acc], line, col + 1) + ["(", ..rest] -> + do_tokenize(rest, [Token(LParen, line), ..acc], line, col + 1) + [digit, ..rest] if digit == "0" + || digit == "1" + || digit == "2" + || digit == "3" + || digit == "4" + || digit == "5" + || digit == "6" + || digit == "7" + || digit == "8" + || digit == "9" -> read_digits(rest, acc, line, col + 1, digit) + [grapheme, ..] -> + Error(SyntaxError( + reason: "Unexpected grapheme " <> grapheme, + line: line, + column: option.Some(col), + )) + } +} + +fn read_digits( + str: List(String), + acc: List(Token), + line: Int, + col: Int, + digit_acc: String, +) { + case str { + [digit, ..rest] + if digit == "0" + || digit == "1" + || digit == "2" + || digit == "3" + || digit == "4" + || digit == "5" + || digit == "6" + || digit == "7" + || digit == "8" + || digit == "9" + -> read_digits(rest, acc, line, col + 1, digit_acc <> digit) + other -> + case int.parse(digit_acc) { + Ok(int) -> + do_tokenize( + other, + [Int(int, line), ..acc], + line, + col + string.length(digit_acc), + ) + Error(_) -> + Error(SyntaxError( + reason: "Unparseable integer " <> digit_acc, + line: line, + column: option.Some(col), + )) + } + } +} diff --git a/src/kielet/tester.gleam b/src/kielet/tester.gleam new file mode 100644 index 0000000..ec12921 --- /dev/null +++ b/src/kielet/tester.gleam @@ -0,0 +1,44 @@ +import gleam/io +import gleam/string +import kielet.{gettext as g_, ngettext as n_} +import kielet/database +import kielet/language +import simplifile + +pub fn main() { + let db = database.new() + let lang_code = "fi" + + let assert Ok(fi) = simplifile.read_bits("fi.mo") + let assert Ok(lang) = language.load(lang_code, fi) + let db = database.add_language(db, lang) + + print(db, "en") + print(db, "fi") +} + +fn print(db: database.Database, lang_code: String) { + io.debug(g_(db, "Press to activate", lang_code)) + io.debug(string.replace( + n_( + db, + "Press to activate %s button", + "Press to activate %s buttons", + 1, + lang_code, + ), + "%s", + "1", + )) + io.debug(string.replace( + n_( + db, + "Press to activate %s button", + "Press to activate %s buttons", + 5, + lang_code, + ), + "%s", + "5", + )) +} diff --git a/test/kielet_test.gleam b/test/kielet_test.gleam new file mode 100644 index 0000000..3831e7a --- /dev/null +++ b/test/kielet_test.gleam @@ -0,0 +1,12 @@ +import gleeunit +import gleeunit/should + +pub fn main() { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + 1 + |> should.equal(1) +} diff --git a/test/mo_test.gleam b/test/mo_test.gleam new file mode 100644 index 0000000..c5998d5 --- /dev/null +++ b/test/mo_test.gleam @@ -0,0 +1,8 @@ +import gleeunit/should +import kielet/mo +import simplifile + +pub fn parse_test() { + let assert Ok(data) = simplifile.read_bits("./fi.mo") + should.be_ok(mo.parse(data)) +}