rvirding-luerl-772b25e/0000775000232200023220000000000015114130670015364 5ustar debalancedebalancervirding-luerl-772b25e/get_comp_opts.escript0000664000232200023220000000474415114130670021632 0ustar debalancedebalance#! /usr/bin/env escript %% -*- mode: erlang; indent-tabs-mode: nil -*- %% Define a number of compiler options. We first work out the current %% Erlang version and from the we can define the various options. %% Define the makefile variables HAS_MAPS, HAS_FULL_KEYS, %% NEW_REC_CORE, NEW_RAND, HAS_FLOOR, HAS_CEIL and NEW_STACKTRACE %% depending on version of Erlang. main(_) -> Version = otp_release(), CompOpts = comp_opts(Version), file:write_file("comp_opts.mk", "COMP_OPTS = " ++ CompOpts ++ "\n"). %% Get the release number. %% We have stolen the idea and most of the code from rebar3. otp_release() -> case erlang:system_info(otp_release) of [$R,N1|Rest] when is_integer(N1) -> %% If OTP <= R16, take the digits. [N1|Rest]; Rel -> %% If OTP >= 17.x, erlang:system_info(otp_release) returns %% just the major version number. File = filename:join([code:root_dir(),"releases",Rel,"OTP_VERSION"]), case file:read_file(File) of {error, _} -> Rel; {ok, Vsn} -> Size = byte_size(Vsn), %% The shortest vsn string consists of at least %% two digits followed by "\n". Therefore, it's %% safe to assume Size >= 3. case binary:part(Vsn, {Size, -3}) of <<"**\n">> -> binary:bin_to_list(Vsn, {0, Size - 3}); _ -> binary:bin_to_list(Vsn, {0, Size - 1}) end end end. comp_opts(Version) -> Copts0 = "-DERLANG_VERSION=\\\"" ++ Version ++ "\\\"" ++ " ", Copts0 ++ append_copts(Version, [{"17","HAS_MAPS"}, {"18","HAS_FULL_KEYS"}, {"19","NEW_REC_CORE"}, {"19","NEW_RAND"}, {"20","NEW_BOOL_GUARD"}, {"20","HAS_FLOOR"}, {"20","HAS_CEIL"}, {"21","NEW_STACKTRACE"}, {"23","EEP48"}, {"27","OTP27_MAYBE"}]). append_copts(Version, [{Ver,Opt}|Opts]) -> Rest = append_copts(Version, Opts), if Version >= Ver -> "-D" ++ Opt ++ "=true" ++ " " ++ Rest; true -> Rest end; append_copts(_Version, []) -> []. rvirding-luerl-772b25e/include/0000775000232200023220000000000015114130670017007 5ustar debalancedebalancervirding-luerl-772b25e/include/luerl.hrl0000664000232200023220000002163315114130670020646 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl.hrl %% Author : Robert Virding %% Purpose : The basic macros/records for Luerl. %% We include the whole environment in one structure even if fields %% come from logically different parts. This make it easier to pass %% around but does mean that there will be more explicit fiddleling to %% get it right. See block/2 and functioncall/4 for examples of this. -record(luerl, {tabs, %Table table envs, %Environment table usds, %Userdata table fncs, %Function table g, %Global table %% stk=[], %Current stack cs=[], %Current call stack %% meta=[], %Data type metatables rand, %Random state tag, %Unique tag trace_func=none, %Trace function trace_data, %Trace data private=#{} }). %% Table structure. -record(tstruct, {data, %Data table/array free, %Index free list next %Next index }). %% Metatables for atomic datatypes. -record(meta, {nil=nil, boolean=nil, number=nil, string=nil }). %% Frames for the call stack. %% Call return frame -record(call_frame, {func,args, %Function, arguments lvs, %Local variables env, %Environment is=[], %Instructions cont=[] %Continuation }). %% Loop break frame -record(loop_frame, {lvs, %Local variables stk, %Stack env, %Environment is=[], %Instructions cont=[] %Continuation }). %% Current line -record(current_line, {line, %Line file %File name }). %% Return -record(return, {rets %Return values }). %% Data types. -record(tref, {i}). %Table reference, index -define(IS_TREF(T), is_record(T, tref)). -record(table, {a,d=[],meta=nil}). %Table type, array, dict, meta -record(eref, {i}). %Environment reference, index -define(IS_EREF(E), is_record(E, eref)). -record(usdref, {i}). %Userdata reference, index -define(IS_USDREF(U), is_record(U, usdref)). -record(userdata, {d,meta=nil}). %Userdata type, data and meta -record(thread, {}). %Thread type %% There are two function types, the Lua one, and the Erlang one. %% The environment with upvalues is defined when the function is %% referenced and can vary if the function is referenced many %% times. Hence it is in the reference not in the the definition. -record(funref, {i,env=[]}). %Function reference -define(IS_FUNREF(F), is_record(F, funref)). -record(lua_func,{anno=[], %Annotation funrefs=[], %Functions directly referenced lsz, %Local var size %% loc=not_used, %Local var block template esz, %Env var size %% env=not_used, %Local env block template pars, %Parameter types body}). %Code block -define(IS_LUAFUNC(F), is_record(F, lua_func)). -record(erl_func,{code}). %Erlang code (fun) -define(IS_ERLFUNC(F), is_record(F, erl_func)). -record(erl_mfa,{m,f,a}). %Erlang code (MFA) -define(IS_ERLMFA(F), is_record(F, erl_mfa)). %% Test if it a function, of either sort. -define(IS_FUNCTION(F), (?IS_FUNREF(F) orelse ?IS_ERLFUNC(F) orelse ?IS_ERLMFA(F))). %% Testing for integers/integer floats or booleans. -define(IS_FLOAT_INT(N), (round(N) == N)). -define(IS_FLOAT_INT(N,I), ((I=round(N)) == N)). -define(IS_TRUE(X), (((X) =/= nil) and ((X) =/= false))). %% %% Global type specifications. %% %% The basic Luerl state. -type luerlstate() :: #luerl{}. %% Luerl data. -type luerldata() :: nil | boolean() | binary() | number() | #tref{} | %Table reference #usdref{} | %Userdata reference #eref{} | %Environment reference #funref{} | %Lua function reference #erl_func{} | %Erlang function #erl_mfa{}. %Erlang Mod, Func, Arg. %% Different methods for storing tables in the global data #luerl{}. %% Access through macros to allow testing with different storage %% methods. This is inefficient with ETS tables where it would %% probably be better to use bags and access with match/select. %% Set which table store to use. We check if we have full maps before %% we use them just to protect ourselves. -ifdef(HAS_FULL_KEYS). -define(TS_USE_MAPS, true). -else. -define(TS_USE_ARRAY, true). -endif. %% -define(TS_USE_ARRAY, true). -ifdef(TS_USE_MAPS). -define(MAKE_TABLE(), maps:new()). -define(GET_TABLE(N, Ts), maps:get(N, Ts)). -define(SET_TABLE(N, T, Ts), maps:put(N, T, Ts)). -define(UPD_TABLE(N, Upd, Ts), maps:update_with(N, Upd, Ts)). -define(DEL_TABLE(N, Ts), maps:remove(N, Ts)). -define(CHK_TABLE(N, Ts), maps:is_key(N, Ts)). -define(FILTER_TABLES(Pred, Ts), maps:filter(Pred, Ts)). -define(FOLD_TABLES(Fun, Acc, Ts), maps:fold(Fun, Acc, Ts)). -endif. -ifdef(TS_USE_ARRAY). %% Use arrays to handle tables. We leave the default value as undefined. -define(MAKE_TABLE(), array:new()). -define(GET_TABLE(N, Ar), array:get(N, Ar)). -define(SET_TABLE(N, T, Ar), array:set(N, T, Ar)). -define(UPD_TABLE(N, Upd, Ar), array:set(N, (Upd)(array:get(N, Ar)), Ar)). -define(DEL_TABLE(N, Ar), array:reset(N, Ar)). -define(CHK_TABLE(N, Ar), ((N >= 0) andalso (array:get(N, Ar) =/= undefined))). -define(FILTER_TABLES(Pred, Ar), ((fun (___Def) -> ___Fil = fun (___K, ___V) -> case Pred(___K, ___V) of true -> ___V; false -> ___Def end end, array:sparse_map(___Fil, Ar) end)(array:default(Ar)))). -define(FOLD_TABLES(Fun, Acc, Ar), array:sparse_foldl(Fun, Acc, Ar)). -endif. -ifdef(TS_USE_ORDDICT). %% Using orddict to handle tables. -define(MAKE_TABLE(), orddict:new()). -define(GET_TABLE(N, Ts), orddict:fetch(N, Ts)). -define(SET_TABLE(N, T, Ts), orddict:store(N, T, Ts)). -define(UPD_TABLE(N, Upd, Ts), orddict:update(N, Upd, Ts)). -define(DEL_TABLE(N, Ts), orddict:erase(N, Ts)). -define(CHK_TABLE(N, Ts), orddict:is_key(N, Ts)). -define(FILTER_TABLES(Pred, Ts), orddict:filter(Pred, Ts)). -define(FOLD_TABLES(Fun, Acc, Ts), orddict:fold(Fun, Acc, Ts)). -endif. -ifdef(TS_USE_PD). %% Use the process dictionary to handle tables. -define(MAKE_TABLE(), ok). -define(GET_TABLE(N, Pd), get(N)). -define(SET_TABLE(N, T, Pd), put(N, T)). -define(UPD_TABLE(N, Upd, Pd), put(N, (Upd)(get(N)))). -define(DEL_TABLE(N, Pd), erase(N)). -define(CHK_TABLE(N, Pd), (get(N) =/= undefined)). -define(FILTER_TABLES(Pred, Pd), Pd). %This needs work -define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work -endif. -ifdef(TS_USE_ETS). %% Use ETS to handle tables. Must get return values right! -define(MAKE_TABLE(),ets:new(luerl_tables, [set])). -define(GET_TABLE(N, E), ets:lookup_element(E, N, 2)). -define(SET_TABLE(N, T, E), begin ets:insert(E, {N,T}), E end). -define(UPD_TABLE(N, Upd, E), begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}), E end). -define(DEL_TABLE(N, E), begin ets:delete(E, N), E end). -define(FILTER_TABLES(Pred, E), E). %This needs work -define(FOLD_TABLES(Fun, Acc, E), ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end, Acc, E)). -endif. %% Define CATCH to handle deprecated get_stacktrace/0 -ifdef(NEW_STACKTRACE). -define(CATCH(C, E, S), C:E:S ->). -else. -define(CATCH(C, E, S), C:E -> S = erlang:get_stacktrace(),). -endif. %% Define MODULEDOC and DOC to handle module and function documentation. -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. rvirding-luerl-772b25e/README.md0000664000232200023220000000437515114130670016654 0ustar debalancedebalance# Luerl *An implementation of Lua in Erlang* [![Build Status](https://github.com/rvirding/luerl/workflows/Test/badge.svg)](https://github.com/rvirding/luerl/actions) [![Luerl Versions](https://img.shields.io/badge/luerl-1.1-blue.svg)](https://github.com/rvirding/luerl) [![Erlang Versions](https://img.shields.io/badge/erlang-24%20to%2026-blue.svg)](https://github.com/rvirding/luerl/blob/master/.github/workflows/ci.yml) [![Tag](https://img.shields.io/github/tag/rvirding/luerl.svg)](https://github.com/rvirding/luerl/tags) [![Project Logo]( https://github.com/rvirding/luerl/blob/develop/priv/images/logo.png?raw=true)](https://github.com/rvirding/luerl/priv/images/logo-large.png) **Alert**: The migration from Lua 5.2 to 5.3 is very much Work-In-Progress. Please test it but there are as yet no guarantees. ## About Luerl is an implementation of standard Lua 5.3 written in Erlang/OTP. Lua is a powerful, efficient, lightweight, embeddable scripting language common in games, IoT devices, AI bots, machine learning and scientific computing research. It supports procedural, object-oriented, functional, data-driven, reactive, organizational programming and data description. Being an extension language, Lua has no notion of a "main" program: it works as a library embedded in a host simple called the embedding program. The host program can invoke functions to execute a piece of Lua code, can write and read Lua variables, and can call Erlang functions by Lua code. Through the use of Erlang functions, Luerl can be augmented to cope with a wide range of different domains, creating a customized language sharing a syntactical framework. Luerl is implemented as a library, written in clean Erlang/OTP. For more information, read the [documentation](https://github.com/rvirding/luerl/wiki) and follow the [get started](https://github.com/rvirding/luerl/wiki/0.2-Getting-started) tutorial. You may also browse the [examples](https://github.com/rvirding/luerl/tree/develop/examples). ## Join the Community [Luerl on Discord](https://discord.gg/Y9Ww5wBhZh) [Luerl Forum - Erlang Forums](https://erlangforums.com/luerl) Luerl embraces both [#Erlang](https://twitter.com/hashtag/erlang?src=hash) and [#LuaLang](https://twitter.com/hashtag/lualang?src=hash) communities and ecosystems. rvirding-luerl-772b25e/rebar.config0000664000232200023220000000251115114130670017645 0ustar debalancedebalance%% -*- mode: erlang; indent-tabs-mode: nil -*- {erl_opts, [{debug_info, true}]}. {deps, []}. {shell, [ % {config, "config/sys.config"}, {apps, [luerl]} ]}. {relx, [{release, {luerl, "1.5.1"}, [kernel, stdlib, sasl, inets, luerl]}, {dev_mode, false}, {include_erts, true}, {extended_start_script, true}]}. {profiles, [ {test, [ {dist_node, [ {setcookie, 'ct'}, {sname, 'ct'} ]}, {ct_opts, [{logdir, "logs"}]} ]}, {otp_24, [ {xrl_opts, []}, {yrl_opts, []} ]}, {default, [ {xrl_opts, [{deterministic, true}]}, {yrl_opts, [{deterministic, true}]} ]} ]}. {project_plugins, [ rebar3_hex, {rebar3_ex_doc, "0.2.28"} ]}. {hex, [ {doc, #{provider => ex_doc}} ]}. {ex_doc, [ {source_url, <<"https://github.com/rvirding/luerl">>}, {extras, ["README.md", "CHANGELOG.md", "LICENSE"]}, {main, "readme"}, {logo, "priv/images/logo.png"}, {skip_undefined_reference_warnings_on, ["luerl_parse", "luerl_scan"]}, {groups_for_modules, [ {"Core API", [ luerl, %% luerl_old, luerl_sandbox ]}, {"Elixir API", [ 'Elixir.Luerl' %% 'Elixir.Luerl.Old' ]} ]} ]}. rvirding-luerl-772b25e/LICENSE0000664000232200023220000002613615114130670016401 0ustar debalancedebalance Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. rvirding-luerl-772b25e/CHANGELOG.md0000664000232200023220000000355715114130670017207 0ustar debalancedebalance# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased ## [1.5.1] ### Fixed - badges and logo in documentation - illegal_token displaying integer value instead of characters ## [1.5.0] ### Changed - luerl_io with some basic io functions added - main Luerl discussions moved from slack to discord - tostring can now handle __tostring and __name metakeys - add ex_doc and move current docs to doc_legacy to avoid collisions ### Fixed - luerl_scan is now Lua compliant and doesn't do utf-8 encoding - cleanup parser and fix handling of local functions - scanner now does Lua compliant numbers and strings - improve handling of string.format - add attributes for local variables - add typespecs to make dialyzer happy - improve documentation info in files - README now links to discord instead - fix pcall error messages ## [1.4.1] ### Fixed - Move `doc/` folder to `doc_legacy/` so it doesn't collide with `ex_doc` artificats ## [1.4.0] This version was not released to Hex.pm due to a build issue ### Changed - (breaking) `luerl:get_private` returns `{ok, Val} | error` tuple ### Fixed - files with only comments can now be loaded - atoms are now decoded as strings - Erlang functions that return errors are now properly propagated upward and state is updated - binary error messages captured in pcall are not formatted [unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.4.1...HEAD [1.3.0]: https://github.com/rvirding/luerl/compare/v1.4.0...v1.4.1 [1.3.0]: https://github.com/rvirding/luerl/compare/v1.3.0...v1.4.0 [1.3.0]: https://github.com/rvirding/luerl/compare/v1.2.3...v1.3.0 [1.2.3]: https://github.com/rvirding/luerl/compare/v1.2.2...v1.2.3 rvirding-luerl-772b25e/VERSION0000664000232200023220000000000615114130670016430 0ustar debalancedebalance1.5.1 rvirding-luerl-772b25e/test/0000775000232200023220000000000015114130670016343 5ustar debalancedebalancervirding-luerl-772b25e/test/luerl_funcall_tests.erl0000664000232200023220000001740015114130670023122 0ustar debalancedebalance%%% @author Hans-Christian Esperer %%% @copyright (C) 2015, Hans-Christian Esperer %%% 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. %%% %%% @doc %% %%% @end %%% Created : 11 Jan 2015 by Hans-Christian Esperer -module(luerl_funcall_tests). -include_lib("eunit/include/eunit.hrl"). -import(luerl_lib, [lua_error/2]). -export([bad_return_value/3]). external_fun_test() -> State = luerl:init(), F = fun(Args, S) -> %% Must decode the args and encode the return value. [A] = luerl:decode_list(Args, S), luerl:encode_list([A + 2, [A + 3, A + 4]], S) end, {ok, State1} = luerl:set_table_keys_dec([<<"testFun">>], F, State), Chunk = <<"function test(i)\n" " local a, b = testFun(i)\n" " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" "end\n">>, {ok, _, State2} = luerl:do(Chunk, State1), {ok, Res, _State3} = luerl:call_function_dec([test], [2], State2), [BoolVal, BoolVal2, BoolVal3] = Res = [true,true,true], ?assertEqual(true, BoolVal), ?assertEqual(true, BoolVal2), ?assertEqual(true, BoolVal3). %% If an error happens in a function call inside Erlang, %% allow the user to propagate it up external_error_test() -> State = luerl:init(), F = fun([Func], S) -> case luerl:call(Func, [], S) of {ok, Ret, S1} -> {Ret, S1}; Error -> Error end end, {ok, State1} = luerl:set_table_keys_dec([<<"foo">>], F, State), Chunk = <<"global = 1\n" "local success, message =\n" " pcall(function()\n" " return foo(function()\n" " global = 2\n" " error(\"whoopsie\")\n" " return \"yay\"\n" " end)\n" "end)\n" "return global, success, message\n">>, {ok, [Global, Success, Message], _State2} = luerl:do(Chunk, State1), ?assertEqual(Global, 2), ?assertEqual(Success, false), ?assertEqual(Message, <<"whoopsie">>). pcall_error_test() -> State = luerl:init(), F = fun([Message], S) -> lua_error(Message, S) end, {ok, State1} = luerl:set_table_keys_dec([<<"foo">>], F, State), ?assertMatch({lua_error, <<"one">>, _State}, luerl:call_function_dec([foo], [<<"one">>], State1)), ?assertMatch({lua_error, <<"two">>, _State}, luerl:do(<<"return foo(\"two\")">>, State1)), ?assertMatch({ok, [false, <<"three!">>], _State}, luerl:do(<<"return pcall(function()\nreturn foo(\"three\")\nend)\n">>, State1)). bad_return_value_test() -> State = luerl:init(), F = fun(_Args, S) -> {bad_value, S} end, {ok, State1} = luerl:set_table_keys_dec([<<"foo">>], F, State), ?assertMatch({lua_error, illegal_return_value, _State}, luerl:call_function_dec([foo], [], State1)), Chunk = <<"return foo();">>, ?assertMatch({lua_error, illegal_return_value, _State}, luerl:do_dec(Chunk, State1)). propagate_error_mfa_pcall_test() -> State = luerl:init(), {ok, State1} = luerl:set_table_keys_dec([<<"foo">>], {luerl_funcall_tests, bad_return_value, []}, State), ?assertMatch({lua_error, something_bad_happened, _State}, luerl:call_function_dec([foo], [], State1)), Chunk = <<"global = 1\n" "local success, message =\n" "pcall(function()\n" " global = 2\n" " return foo();\n" "end)\n" "return global, success, message\n">>, ?assertMatch({ok, [2, false, <<"something_bad_happened!">>], _State}, luerl:do_dec(Chunk, State1)). bad_return_value_pcall_test() -> State = luerl:init(), F = fun(_Args, S) -> {bad_value, S} end, {ok, State1} = luerl:set_table_keys_dec([<<"foo">>], F, State), ?assertMatch({lua_error, illegal_return_value, _State}, luerl:call_function_dec([foo], [], State1)), Chunk = <<"global = 1\n" "local success, message =\n" "pcall(function()\n" " global = 2\n" " return foo();\n" "end)\n" "return global, success, message\n">>, ?assertMatch({ok, [2, false, <<"illegal format of return value">>], _State}, luerl:do_dec(Chunk, State1)). return_lib_function_test() -> State = luerl:init(), {ok, _, State1} = luerl:do(<<"function test()\n return string.find end\n">>, State), {ok, [{M,F,A}], _State2} = luerl:call_function_dec([test], [1], State1), {Res, _State3} = apply(M, F, [A, [<<"barfooblafasel">>, <<"foo">>], State1]), ?assertEqual([4, 6], Res). define_fun_in_lua_test() -> State = luerl:init(), Chunk = <<"function mkadder(incby)\n" " return function(i)\n" " print(\"Call into Luerl!\")\n" " return i + incby\n" " end\n" "end\n">>, {ok, _, State1} = luerl:do(Chunk, State), {ok, [Fun2], State2} = luerl:call_function_dec([mkadder], [1], State1), {ok, [Fun3], State3} = luerl:call_function_dec([mkadder], [2], State1), %% Should really decode the return value, but it is only a number. ?assertMatch({[5], _}, Fun2([4], State2)), ?assertMatch({[5.0],_}, Fun2([4.0], State2)), ?assertMatch({[6], _}, Fun3([4], State3)). define_fun2_in_lua_test() -> State = luerl:init(), Chunk = <<"function mklist(numentries)\n" " return function(entryval)\n" " local list = {}\n" " for i = 1,numentries do\n" " list[i] = entryval\n" " end\n" " return list\n" " end\n" "end\n">>, {ok, _, State1} = luerl:do(Chunk, State), %% Build a luerl function and safely call it. {Emklist, St2} = luerl:encode_list([mklist], State1), {ok, [Efunc5], St3} = luerl:call_function(Emklist, [5], St2), {ok, Res20, St4} = luerl:call_function(Efunc5, [4], St3), ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], luerl:decode_list(Res20, St4)), %% Build an Erlang fun and just unsafely call it. {ok, [Fun2], State2} = luerl:call_function_dec([mklist], [5], State1), {Res21,State21} = Fun2([4], State2), ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], luerl:decode_list(Res21, State21)), {Res22,State22} = Fun2([4.0], State2), ?assertMatch([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], luerl:decode_list(Res22, State22)), {ok, [Fun3], State3} = luerl:call_function_dec([mklist], [10], State1), {Res3, State31} = Fun3([4], State3), ?assertMatch([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], luerl:decode_list(Res3, State31)). newindex_metamethod_test() -> State = luerl:init(), Chunk = <<"local t = {}\n" "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" "t[key] = value\n" "end})\n\n" "m[123] = 456\n" "return t[123], m[123]">>, {ok, [TVal, MVal], _State1} = luerl:do_dec(Chunk, State), ?assertEqual(456, TVal), ?assertEqual(nil, MVal). bad_return_value(_Arg, _Args, State) -> lua_error(something_bad_happened, State). rvirding-luerl-772b25e/test/luerl_old_funcall_tests.erl0000664000232200023220000000766115114130670023770 0ustar debalancedebalance%%% @author Hans-Christian Esperer %%% @copyright (C) 2015, Hans-Christian Esperer %%% 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. %%% %%% @doc %% %%% @end %%% Created : 11 Jan 2015 by Hans-Christian Esperer -module(luerl_old_funcall_tests). -include_lib("eunit/include/eunit.hrl"). external_fun_test() -> State = luerl_old:init(), F = fun([A], S) -> {[A + 2, [A + 3, A + 4]], S} end, State1 = luerl_old:set_table([<<"testFun">>], F, State), {_, State2} = luerl_old:do(<<"function test(i)\n local a, b = testFun(i)\n return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4) end">>, State1), {Res, _State3} = luerl_old:call_function([test], [2], State2), [BoolVal, BoolVal2, BoolVal3] = Res, ?assertEqual(true, BoolVal), ?assertEqual(true, BoolVal2), ?assertEqual(true, BoolVal3). external_nostate_fun_test() -> State = luerl_old:init(), F = fun([A]) -> [A + 2, [A + 3, A + 4]] end, State1 = luerl_old:set_table([<<"testFun">>], F, State), Chunk = <<"function test(i)\n" " local a, b = testFun(i)\n" " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" "end">>, {_, State2} = luerl_old:do(Chunk, State1), {Res, _State3} = luerl_old:call_function([test], [2], State2), [BoolVal, BoolVal2, BoolVal3] = Res, ?assertEqual(true, BoolVal), ?assertEqual(true, BoolVal2), ?assertEqual(true, BoolVal3). return_lib_function_test() -> State = luerl_old:init(), {_, State1} = luerl_old:do(<<"function test()\n return string.find end\n">>, State), {[{M,F,A}], _State2} = luerl_old:call_function([test], [1], State1), {Res, _State3} = apply(M, F, [A, [<<"barfooblafasel">>, <<"foo">>], State1]), ?assertEqual([4, 6], Res). define_fun_in_lua_test() -> State = luerl_old:init(), Chunk = <<"function mkadder(incby)\n" " return function(i)\n" " print(\"Call into Luerl!\")\n" " return i + incby\n" " end\n" "end\n">>, {_, State1} = luerl_old:do(Chunk, State), {[Fun], _State2} = luerl_old:call_function([mkadder], [1], State1), {[Fun2], _State3} = luerl_old:call_function([mkadder], [2], State1), ?assertEqual([5], Fun([4])), ?assertEqual([5.0], Fun([4.0])), ?assertEqual([6], Fun2([4])). define_fun2_in_lua_test() -> State = luerl_old:init(), Chunk = <<"function mklist(numentries)\n" " return function(entryval)\n" " local list = {}\n" " for i = 1,numentries do\n" " list[i] = entryval\n" " end\n" " return list\n" " end\n" "end\n">>, {_, State1} = luerl_old:do(Chunk, State), {[Fun], _State2} = luerl_old:call_function([mklist], [5], State1), {[Fun2], _State3} = luerl_old:call_function([mklist], [10], State1), ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], Fun([4])), ?assertEqual([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], Fun([4.0])), ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], Fun2([4])). newindex_metamethod_test() -> State = luerl_old:init(), Chunk = <<"local t = {}\n" "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" "t[key] = value\n" "end})\n\n" "m[123] = 456\n" "return t[123], m[123]">>, {[TVal, MVal], _State1} = luerl_old:do(Chunk, State), ?assertEqual(456, TVal), ?assertEqual(nil, MVal). rvirding-luerl-772b25e/test/Elixir.Luerl_tests.erl0000664000232200023220000000207215114130670022610 0ustar debalancedebalance%% Copyright (C) 2024 Robert Virding %% %% 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. -module('Elixir.Luerl_tests'). -include_lib("eunit/include/eunit.hrl"). private_test() -> State1 = 'Elixir.Luerl':init(), State2 = 'Elixir.Luerl':put_private(State1, secret, <<"mysecret">>), ?assertMatch({ok, <<"mysecret">>}, 'Elixir.Luerl':get_private(State2, secret)), ?assertMatch(error, 'Elixir.Luerl':get_private(State2, missing)), State3 = 'Elixir.Luerl':delete_private(State2, secret), ?assertMatch(error, 'Elixir.Luerl':get_private(State3, secret)). rvirding-luerl-772b25e/test/lib_os_SUITE.erl0000664000232200023220000001012115114130670021262 0ustar debalancedebalance%% Copyright (c) 2023 Mark Meeus %% %% 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. -module(lib_os_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -export([all/0, groups/0]). -export([os_date_formatting/1, os_date_table/1, os_date_integrated/1, os_date_integrated_table/1]). all() -> [ {group, date_support}, {group, integrated} ]. groups() -> [ {date_support, [parallel], [os_date_formatting, os_date_table]}, {integrated, [parallel], [os_date_integrated, os_date_integrated_table]} ]. os_date_formatting(_) -> Date = {{2023, 1, 2}, {3, 4, 5}}, ?assertEqual(<<"2023">>, luerl_lib_os_date:format(Date, <<"%Y">>)), ?assertEqual(<<"23">>, luerl_lib_os_date:format(Date, <<"%y">>)), ?assertEqual(<<"02">>, luerl_lib_os_date:format(Date, <<"%d">>)), ?assertEqual(<<"01">>, luerl_lib_os_date:format(Date, <<"%m">>)), ?assertEqual(<<"03">>, luerl_lib_os_date:format(Date, <<"%H">>)), ?assertEqual(<<"04">>, luerl_lib_os_date:format(Date, <<"%M">>)), ?assertEqual(<<"05">>, luerl_lib_os_date:format(Date, <<"%S">>)), ?assertEqual(<<"01/02/23">>, luerl_lib_os_date:format(Date, <<"%x">>)), ?assertEqual(<<"03:04:05">>, luerl_lib_os_date:format(Date, <<"%X">>)), ?assertEqual(<<"1">>, luerl_lib_os_date:format(Date, <<"%w">>)), %% Day of week %% Hour in 12H ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{0,0,0}}, <<"%I">>)), ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{0,59,0}}, <<"%I">>)), ?assertEqual(<<"01">>, luerl_lib_os_date:format({{2023, 1, 2},{1,00,0}}, <<"%I">>)), ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{12,00,0}}, <<"%I">>)), ?assertEqual(<<"11">>, luerl_lib_os_date:format({{2023, 1, 2},{23,00,0}}, <<"%I">>)), ?assertEqual(<<"AM">>, luerl_lib_os_date:format({{2023, 1, 2},{0,0,0}}, <<"%p">>)), ?assertEqual(<<"AM">>, luerl_lib_os_date:format({{2023, 1, 2},{11, 59,0}}, <<"%p">>)), ?assertEqual(<<"PM">>, luerl_lib_os_date:format({{2023, 1, 2},{12,0,0}}, <<"%p">>)), ?assertEqual(<<"PM">>, luerl_lib_os_date:format({{2023, 1, 2},{23,59,0}}, <<"%p">>)), ?assertEqual(<<"09">>, luerl_lib_os_date:format({{2023, 1, 2},{9,0,0}}, <<"%I">>)), %% Hour in 12H ?assertEqual(<<"01">>, luerl_lib_os_date:format(Date, <<"%W">>)), %% ISO Week number ?assertEqual(<<"Jan">>, luerl_lib_os_date:format(Date, <<"%b">>)), ?assertEqual(<<"January">>, luerl_lib_os_date:format(Date, <<"%B">>)), ?assertEqual(<<"Mon">>, luerl_lib_os_date:format(Date, <<"%a">>)), ?assertEqual(<<"Monday">>, luerl_lib_os_date:format(Date, <<"%A">>)), ?assertEqual(<<"%">>, luerl_lib_os_date:format(Date, <<"%%">>)), ?assertEqual(<<"2023-01-02 03:04:05">>, luerl_lib_os_date:format(Date, <<"%Y-%m-%d %H:%M:%S">>)). os_date_table(_) -> Date = {{2023, 1, 2}, {3, 4, 5}}, ?assertEqual([ {<<"year">>, 2023}, {<<"month">>, 1}, {<<"day">>, 2}, {<<"hour">>, 3}, {<<"min">>, 4}, {<<"sec">>, 5}, {<<"wday">>, 2} ], luerl_lib_os_date:format(Date, <<"*t">>)). os_date_integrated(_) -> State = luerl:init(), Chunk = <<"return os.date('noformat'), os.date(), os.date('%c', 1683371767)">>, {ok, [NoFormat, _, FromTimeStamp], _State1} = luerl:do(Chunk, State), ?assertEqual(<<"noformat">>, NoFormat), %% Date is "Sat May 6 13:16:07 2023", %% Just check year to avoid test flakiness ?assert(re:run(FromTimeStamp, <<"2023">>) =/= nomatch). os_date_integrated_table(_) -> State = luerl:init(), Chunk = <<"return os.date('*t').year">>, {ok, [Result], _State1} = luerl:do(Chunk, State), {{Year, _, _}, _} = calendar:local_time(), ?assertEqual(Year, Result). rvirding-luerl-772b25e/test/luerl_old_tests.erl0000664000232200023220000000350215114130670022252 0ustar debalancedebalance%% Copyright (C) 2024 Robert Virding %% %% 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. -module(luerl_old_tests). -include_lib("eunit/include/eunit.hrl"). encode_test() -> State = luerl_old:init(), ?assertMatch({nil, _State}, luerl_old:encode(nil, State)), ?assertMatch({false, _State}, luerl_old:encode(false, State)), ?assertMatch({true, _State}, luerl_old:encode(true, State)), ?assertMatch({<<"binary">>, _State}, luerl_old:encode(<<"binary">>, State)), ?assertMatch({<<"atom">>, _State}, luerl_old:encode(atom, State)), ?assertMatch({5, _State}, luerl_old:encode(5, State)), ?assertMatch({{tref, _}, _State}, luerl_old:encode(#{a => 1, b => 2}, State)), ?assertMatch({{tref, _}, _State}, luerl_old:encode([{a,1},{b,2}], State)). encode_error_test() -> State = luerl_old:init(), ?assertException(error, {badarg, _}, luerl_old:encode({a,1}, State)). encode_table_test() -> {Table, State} = luerl_old:encode(#{a => 1}, luerl_old:init()), State1 = luerl_old:set_table1([<<"foo">>], Table, State), ?assertMatch({Table, _State2}, luerl_old:get_table1([<<"foo">>], State1)), ?assertMatch({tref, _}, Table). invalid_value_test() -> State = luerl_old:init(), ?assertException(error, {badarg, {invalid, value}}, luerl_old:encode({invalid, value}, State)). rvirding-luerl-772b25e/test/luerl_scan_tests.erl0000664000232200023220000000151715114130670022424 0ustar debalancedebalance%% Copyright (C) 2025 Robert Virding, Dave Lucia %% %% 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. -module(luerl_scan_tests). -include_lib("eunit/include/eunit.hrl"). syntax_error_test() -> State = luerl:init(), ?assertMatch({error, [{1,luerl_scan, {user,"syntax error near '\"'"}}], []}, luerl:do(<<"print(\"hi)">>, State)). rvirding-luerl-772b25e/test/luerl_time_SUITE.erl0000664000232200023220000000357715114130670022175 0ustar debalancedebalance-module(luerl_time_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -export([all/0]). -export([os_date/1]). -if(?OTP_RELEASE >= 25). -define(START_NODE(Name, Env), (fun(Name, Env) -> case ?CT_PEER(#{name => Name, env => Env}) of {ok, _PeerPid, Node} -> {ok, Node}; Err = {error, _Reason, _NodeName} -> Err end end)(Name, Env)). -else. -define(START_NODE(Name, Env), (fun(Name, Env) -> ct_slave:start(Name, [{env, Env}, {monitor_master, true}]) end)(Name, Env)). -endif. all() -> lists:flatten([windows_tests(), linux_tests()]). os_date(_Config) -> {ok, LusakaNode} = ?START_NODE(africa_lusaka, [{"TZ", "Africa/Lusaka"}]), ok = set_path(LusakaNode), {ok, LondonNode} = ?START_NODE(europe_london, [{"TZ", "Europe/London"}]), ok = set_path(LondonNode), LusakaLocalTime = rpc:call(LusakaNode, calendar, local_time, []), LondonLocalTime = rpc:call(LondonNode, calendar, local_time, []), ?assertNotMatch({badrpc, _}, LusakaLocalTime), ?assertNotMatch({badrpc, _}, LondonLocalTime), ?assert(LusakaLocalTime =/= LondonLocalTime), ?assertMatch({ok,[<<"Sat May 6 13:16:07 2023">>], _St1}, rpc:call(LusakaNode, luerl, do_dec, ["return os.date('%c', 1683371767)", luerl:init()])), ?assertMatch({ok,[<<"Sat May 6 12:16:07 2023">>], _St2}, rpc:call(LondonNode, luerl, do_dec, ["return os.date('%c', 1683371767)", luerl:init()])), ok. windows_tests() -> []. linux_tests() -> [os_date]. set_path(Node) -> [case rpc:call(Node, code, add_path, [Path]) of true -> ok; Err = {error, _} -> throw({badpath, Path, Err}) end || Path <- code:get_path(), filelib:is_dir(Path)], ok. rvirding-luerl-772b25e/test/luerl_return_SUITE.erl0000664000232200023220000000532215114130670022544 0ustar debalancedebalance%% Copyright (c) 2019 Ferenc Boroczki %% %% 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. -module(luerl_return_SUITE). -include_lib("common_test/include/ct.hrl"). -export([all/0, groups/0, init_per_suite/1, end_per_suite/1]). -export([simple_return/1, fun_return/1, use_lib/1, variable_args/1, check_unicode/1, table_tests/1]). init_per_suite(Config) -> DataDir = ?config(data_dir, Config), os:putenv("LUA_PATH", DataDir ++ "?.lua;" ++ DataDir ++ "?/init.lua"), Config. end_per_suite(Config) -> Config. all() -> [ {group, return} ]. groups() -> [ {return, [parallel], [simple_return, fun_return, use_lib, variable_args, check_unicode, table_tests]} ]. simple_return(Config) -> Tests = [ {"simple_return_1.lua", [1]}, {"simple_return_multi.lua", [1, <<"string 2">>, 3.4]} ], run_tests(Config, Tests). fun_return(Config) -> run_and_check(Config, "fun_return_multi.lua", [7, <<"str 1">>, 5.5, 11.0]). use_lib(Config) -> LuaDecimal = fun(B, E) -> [{<<"b">>, B}, {<<"e">>, E}] end, Expected = [LuaDecimal(B, E) || {B, E} <- [{13, 1}, {7, 1}, {3, 3}]], run_and_check(Config, "decimal_test.lua", Expected). variable_args(Config) -> run_tests(Config, [ {"variable_args_1.lua", [99, 88, 77]}, {"variable_args_multi.lua", [9, <<"banana">>, 8]} ]). check_unicode(Config) -> St = run_and_check(Config, "check_unicode.lua", []), check_unicode_call_fun(<<"árvíztűrő tükörfúrógép"/utf8>>, 31, check_hun, St), check_unicode_call_fun(<<"λ"/utf8>>, 2, check_lambda, St), check_unicode_call_fun(<<9810/utf8>>, 3, check_aquarius, St). check_unicode_call_fun(Input, Length, LuaFun, St) -> {ok, [Input, Input, true, Length, Length], _} = luerl:call_function_dec([LuaFun], [Input], St). table_tests(Config) -> run_and_check(Config, "table_indexed_table.lua", [111, 222, 333]). run_tests(Config, Tests) -> [run_and_check(Config, Script, Expected) || {Script, Expected} <- Tests]. run_and_check(Config, Script, Expected) -> DataDir = ?config(data_dir, Config), ScriptFile = DataDir ++ Script, {ok, Result, St} = luerl:dofile_dec(ScriptFile, luerl:init()), {true, {expected, Expected}, {result, Result}} = {Result =:= Expected, {expected, Expected}, {result, Result}}, St. rvirding-luerl-772b25e/test/luerl_tests.erl0000664000232200023220000000447715114130670021430 0ustar debalancedebalance%% Copyright (C) 2024 Robert Virding %% %% 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. -module(luerl_tests). -include_lib("eunit/include/eunit.hrl"). encode_test() -> State = luerl:init(), ?assertMatch({nil, _State}, luerl:encode(nil, State)), ?assertMatch({false, _State}, luerl:encode(false, State)), ?assertMatch({true, _State}, luerl:encode(true, State)), ?assertMatch({<<"binary">>, _State}, luerl:encode(<<"binary">>, State)), ?assertMatch({<<"atom">>, _State}, luerl:encode(atom, State)), ?assertMatch({5, _State}, luerl:encode(5, State)), ?assertMatch({{tref, _}, _State}, luerl:encode(#{a => 1, b => 2}, State)), ?assertMatch({{tref, _}, _State}, luerl:encode([{a,1},{b,2}], State)). encode_error_test() -> State = luerl:init(), ?assertException(error, {badarg, _}, luerl:encode({a,1}, State)). encode_table_test() -> {Table, State} = luerl:encode(#{a => 1}, luerl:init()), {ok, State1} = luerl:set_table_keys([<<"foo">>], Table, State), ?assertMatch({ok, Table, _State2}, luerl:get_table_keys([<<"foo">>], State1)), ?assertMatch({tref, _}, Table). invalid_value_test() -> State = luerl:init(), ?assertException(error, {badarg, {invalid, value}}, luerl:encode({invalid, value}, State)). private_test() -> State1 = luerl:init(), State2 = luerl:put_private(secret, <<"mysecret">>, State1), ?assertMatch(<<"mysecret">>, luerl:get_private(secret, State2)), ?assertException(error, {badkey, missing}, luerl:get_private(missing, State2)), State3 = luerl:delete_private(secret, State2), ?assertException(error, {badkey, secret}, luerl:get_private(secret, State3)). loadfile_only_comments_test() -> State1 = luerl:init(), ?assertMatch({ok, _, _}, luerl:loadfile("./test/luerl_return_SUITE_data/only_comments.lua", State1)). rvirding-luerl-772b25e/test/luerl_return_SUITE_data/0000775000232200023220000000000015114130670023027 5ustar debalancedebalancervirding-luerl-772b25e/test/luerl_return_SUITE_data/simple_return_1.lua0000664000232200023220000000001115114130670026632 0ustar debalancedebalancereturn 1 rvirding-luerl-772b25e/test/luerl_return_SUITE_data/decimal_test.lua0000664000232200023220000000133415114130670026170 0ustar debalancedebalancelocal d = require("decimal") a = d.new(1, 2) -- 100 b = d.new(3, 1) -- 30 print(a, "+", b, "=", a + b) print(a, "-", b, "=", a - b) print(a, "*", b, "=", a * b) print(a, "/", b, "=", a / b) x1 = d.new(1, 2) x2 = d.new(2, 2) x3 = d.new(2, 1) x4 = d.new(10, 1) x5 = d.new(10, 2) print(a, "==", b, "=", a == b) print(a, "==", x1, "=", a == x1) print(a, "==", x2, "=", a == x2) print(a, "==", x3, "=", a == x3) print(a, "==", x4, "=", a == x4) print(a, "==", x5, "=", a == x5) assert(a + b == d.new(130, 0)) assert(a + b == d.new(13, 1)) assert(a - b == d.new(70, 0)) assert(a - b == d.new(7, 1)) assert(a * b == d.new(3000, 0)) assert(a * b == d.new(3, 3)) --assert(a / b == d.new(33333333333333, -15)) return a + b, a - b, a * b rvirding-luerl-772b25e/test/luerl_return_SUITE_data/decimal.lua0000664000232200023220000000220415114130670025126 0ustar debalancedebalancelocal Decimal = {} Decimal.mt = { __tostring = function(d) return d.b .. "e" .. d.e end, __add = function(a, b) a, b = Decimal.same_e(a, b) if a.e == b.e then return Decimal.new(a.b + b.b, a.e) end end, __sub = function(a, b) return a + Decimal.new(-b.b, b.e) end, __mul = function(a, b) return Decimal.new(a.b * b.b, a.e + b.e) end, __div = function(a, b) -- @todo fix return Decimal.new(a.b / b.b, a.e - b.e) end, __eq = function(a, b) a, b = Decimal.same_e(a, b) return a.b == b.b and a.e == b.e end } Decimal.new = function(b, e) local d = { b = b, e = e } setmetatable(d, Decimal.mt) return d end Decimal.change_e = function(d, e) if d.e == e then return d end if d.e > e then return Decimal.change_e(Decimal.new(10 * d.b, d.e - 1), e) end error("can't increment exponential") end Decimal.same_e = function(a, b) local min_e = math.min(a.e, b.e) return Decimal.change_e(a, min_e), Decimal.change_e(b, min_e) end return Decimal rvirding-luerl-772b25e/test/luerl_return_SUITE_data/only_comments.lua0000664000232200023220000000003215114130670026413 0ustar debalancedebalance-- No code, just comments rvirding-luerl-772b25e/test/luerl_return_SUITE_data/table_indexed_table.lua0000664000232200023220000000015215114130670027466 0ustar debalancedebalancetk1 = { a = 1 } tk2 = { b = 2 } t = {} t[tk1] = 111 t[tk2] = 222 t[t] = 333 return t[tk1], t[tk2], t[t] rvirding-luerl-772b25e/test/luerl_return_SUITE_data/variable_args_multi.lua0000664000232200023220000000054115114130670027545 0ustar debalancedebalancefunction make_table(...) local t = {} for i = 1, select('#', ...), 2 do k = select(i, ...) v = select(i + 1, ...) t[k] = v end return t end print(make_table) local tab = make_table("x", 9, 7, "banana", "z", 8) assert(tab["x"] == 9) assert(tab[7] == "banana") assert(tab.z == 8) return tab["x"], tab[7], tab.z rvirding-luerl-772b25e/test/luerl_return_SUITE_data/variable_args_1.lua0000664000232200023220000000053715114130670026560 0ustar debalancedebalancefunction get_from_table(Map, Key, ...) local Value = Map[Key] if select('#', ...) > 0 then return get_from_table(Value, ...) else return Value end end local tab = { a = { x1 = { x2 = 99 } }, b = 88, c = { d = 77 } } return get_from_table(tab, "a", "x1", "x2"), get_from_table(tab, "b"), get_from_table(tab, "c", "d") rvirding-luerl-772b25e/test/luerl_return_SUITE_data/check_unicode.lua0000664000232200023220000000125015114130670026313 0ustar debalancedebalancefunction check_hun(erl_str) local lua_str = "árvíztűrő tükörfúrógép" return check_values(erl_str, lua_str, 31) end function check_lambda(erl_str) local lua_str = "λ" return check_values(erl_str, lua_str, 2) end function check_aquarius(erl_str) local lua_str = utf8.char(9810) return check_values(erl_str, lua_str, 3) end function check_values(erl_str, lua_str, length) assert(string.len(lua_str) == length, "invalid lua length") assert(string.len(erl_str) == length, "invalid erl length") assert(lua_str == erl_str, "different values") return erl_str, lua_str, erl_str == lua_str, string.len(erl_str), string.len(lua_str) end rvirding-luerl-772b25e/test/luerl_return_SUITE_data/fun_return_multi.lua0000664000232200023220000000022615114130670027133 0ustar debalancedebalancefunction retfun(value) return value end function retfun2(value) return value, 2 * value end return retfun(7), retfun("str 1"), retfun2(5.5) rvirding-luerl-772b25e/test/luerl_return_SUITE_data/simple_return_multi.lua0000664000232200023220000000003215114130670027627 0ustar debalancedebalancereturn 1, "string 2", 3.4 rvirding-luerl-772b25e/Emakefile0000664000232200023220000000006315114130670017170 0ustar debalancedebalance%% -*- erlang -*- {'src/luerl*',[{outdir,ebin}]}. rvirding-luerl-772b25e/ebin/0000775000232200023220000000000015114130670016301 5ustar debalancedebalancervirding-luerl-772b25e/ebin/luerl.app0000664000232200023220000000254715114130670020136 0ustar debalancedebalance{application,luerl, [{description,"Luerl - an implementation of Lua on Erlang"}, {vsn,"1.5.1"}, {modules,['Elixir.Luerl.New','Elixir.Luerl',luerl,luerl_anno, luerl_app,luerl_comp,luerl_comp_cg,luerl_comp_env, luerl_comp_lint,luerl_comp_locf,luerl_comp_normalise, luerl_comp_peep,luerl_comp_vars,luerl_emul,luerl_heap, luerl_lib,luerl_lib_basic,luerl_lib_bit32, luerl_lib_debug,luerl_lib_io,luerl_lib_math, luerl_lib_os,luerl_lib_os_date,luerl_lib_package, luerl_lib_string,luerl_lib_string_format, luerl_lib_table,luerl_lib_utf8,luerl_new,luerl_old, luerl_parse,luerl_sandbox,luerl_scan,luerl_sup, luerl_util,ttdict,ttsets]}, {registered,[]}, {applications,[kernel,stdlib]}, {env,[]}, {mod,{luerl_app,[]}}, {licenses,["Apache-2.0"]}, {links,[{"Github","https://github.com/rvirding/luerl"}]}, {files,["README.md","LICENSE","VERSION","src","doc", "ebin/luerl.app","include","rebar.*","*akefile", "*.escript"]}, {exclude_files,["priv/images/*"]}]}. rvirding-luerl-772b25e/Makefile0000664000232200023220000001165615114130670017035 0ustar debalancedebalance# Copyright (c) 2016-2023 Robert Virding # # 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. # Makefile for Luerl # Building from .xrl, .yrl and .erl # Intermediaries from leex and yecc stay in ./src BINDIR = ./bin EBINDIR = ./ebin SRCDIR = ./src LIB = luerl # To run erl as bash FINISH = -run init stop -noshell # Scripts to be evaluated GET_VERSION = '{ok,[App]}=file:consult("src/$(LIB).app.src"), \ V=proplists:get_value(vsn,element(3,App)), \ io:format("~p~n",[V])' \ $(FINISH) ## The .erl, .xrl, .yrl and .beam files ESRCS := $(notdir $(wildcard $(SRCDIR)/*.erl)) XSRCS := $(notdir $(wildcard $(SRCDIR)/*.xrl)) YSRCS := $(notdir $(wildcard $(SRCDIR)/*.yrl)) EBINS = $(ESRCS:.erl=.beam) $(XSRCS:.xrl=.beam) $(YSRCS:.yrl=.beam) ERLCFLAGS = -W1 +debug_info ERLC ?= erlc all: compile .PHONY: all compile clean echo examples debug docs compile: comp_opts.mk $(addprefix $(EBINDIR)/, $(EBINS)) $(EBINDIR)/%.beam: $(SRCDIR)/%.erl $(SRCDIR)/luerl.hrl comp_opts.mk @ mkdir -p $(EBINDIR) $(ERLC) $(ERLCFLAGS) -o $(EBINDIR) $(COMP_OPTS) $(ERLCFLAGS) $< %.erl: %.xrl $(ERLC) -o $(SRCDIR) $< %.erl: %.yrl $(ERLC) -o $(SRCDIR) $< comp_opts.mk: get_comp_opts.escript escript get_comp_opts.escript -include comp_opts.mk clean: @ rm -f $(EBINDIR)/*.beam @ rm -f *.beam @ rm -f erl_crash.dump @ rm comp_opts.mk $(MAKE) -C examples clean clean-all: clean rm -rf _build echo: echo $(OBJECTS) get-version: @echo @echo "Getting version info ..." @echo @echo -n app.src: '' @erl -eval $(GET_VERSION) examples: all $(MAKE) -C examples debug: ERLCFLAGS="+debug_info" make all ############### ### TESTING ### ############### # XXX for some reason, the first pass of eunit doesn't run the tests?! eunit: @rebar3 as test do compile,eunit,eunit common-test: @rebar3 as test do compile,ct ct: common-test tests: @rebar3 as test do compile,eunit,eunit,ct # this protects the intermediate .erl files from make's auto deletion #.SECONDARY: $(XRL_INTERM) $(YRL_INTERM) ##################### ### DOCUMENTATION ### ##################### # Targets for generating docs and man pages DOCDIR = doc_legacy DOCSRC = $(DOCDIR)/src MANDIR = $(DOCDIR)/man PDFDIR = $(DOCDIR)/pdf EPUBDIR = $(DOCDIR)/epub MANINSTDIR ?= $(PREFIX)/share/man MAN1_SRCS = $(notdir $(wildcard $(DOCSRC)/*1.md)) MAN1S = $(MAN1_SRCS:.1.md=.1) TXT1S = $(MAN1_SRCS:.1.md=.txt) PDF1S = $(MAN1_SRCS:.1.md=.pdf) MAN3_SRCS = $(notdir $(wildcard $(DOCSRC)/*3.md)) MAN3S = $(MAN3_SRCS:.3.md=.3) PDF3S = $(MAN3_SRCS:.3.md=.pdf) TXT3S = $(MAN3_SRCS:.3.md=.txt) MAN7_SRCS = $(notdir $(wildcard $(DOCSRC)/*7.md)) MAN7S = $(MAN7_SRCS:.7.md=.7) TXT7S = $(MAN7_SRCS:.7.md=.txt) PDF7S = $(MAN7_SRCS:.7.md=.pdf) # For pandoc for generating PDFs as it omly accepts a few options. # xelatex is a reasonable default or wkhtmltopdf. PANDOCPDF ?= xelatex # Just generate the docs that are tracked in git docs: docs-txt docs-man: \ $(addprefix $(MANDIR)/, $(MAN1S)) \ $(addprefix $(MANDIR)/, $(MAN3S)) \ $(addprefix $(MANDIR)/, $(MAN7S)) $(MANDIR)/%.1: $(DOCSRC)/%.1.md pandoc -f markdown -s -t man -o $@ $< $(MANDIR)/%.3: $(DOCSRC)/%.3.md pandoc -f markdown -s -t man -o $@ $< $(MANDIR)/%.7: $(DOCSRC)/%.7.md pandoc -f markdown -s -t man -o $@ $< clean-docs: rm -f $(DOCDIR)/*.txt $(MANDIR)/*.[0-9] $(PDFDIR)/*.pdf $(EPUBDIR)/*.epub docs-txt: docs-man \ $(addprefix $(DOCDIR)/, $(TXT1S)) \ $(addprefix $(DOCDIR)/, $(TXT3S)) \ $(addprefix $(DOCDIR)/, $(TXT7S)) @if [ -f $(DOCDIR)/luerl_guide.txt ]; then \ cp $(DOCDIR)/luerl_guide.txt $(DOCDIR)/user_guide.txt ; \ fi $(DOCDIR)/%.txt: export GROFF_NO_SGR=1 $(DOCDIR)/%.txt: $(MANDIR)/%.1 groff -t -e -mandoc -Tutf8 $< | col -bx > $@ $(DOCDIR)/%.txt: $(MANDIR)/%.3 groff -t -e -mandoc -Tutf8 $< | col -bx > $@ $(DOCDIR)/%.txt: $(MANDIR)/%.7 groff -t -e -mandoc -Tutf8 $< | col -bx > $@ $(PDFDIR): @$(INSTALL_DIR) $(PDFDIR) docs-pdf: $(PDFDIR) \ $(addprefix $(PDFDIR)/, $(PDF1S)) \ $(addprefix $(PDFDIR)/, $(PDF3S)) \ $(addprefix $(PDFDIR)/, $(PDF7S)) $(PDFDIR)/%.pdf: $(DOCSRC)/%.1.md pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< $(PDFDIR)/%.pdf: $(DOCSRC)/%.3.md pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< $(PDFDIR)/%.pdf: $(DOCSRC)/%.7.md pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< ################ ### RELEASES ### ################ hex-publish: clean-all compile rebar3 hex publish package tags: git tag $(shell erl -eval $(GET_VERSION)|tr -d '"') git tag v$(shell erl -eval $(GET_VERSION)|tr -d '"') git push --tags rvirding-luerl-772b25e/rebar.config.script0000664000232200023220000001025415114130670021153 0ustar debalancedebalance%% -*- mode: erlang; indent-tabs-mode: nil -*- Conf0 = CONFIG, %The original config %% Do a deep set stepping down a list of keys replacing/adding last %% with value. Named funs would be nicer but not always available. SetConf = fun ([K], Val, Ps, _F) -> %% Replace the whole K field with Val. [Val|proplists:delete(K, Ps)]; ([K|Ks], Val, Ps, F) -> %% Step down and build coming up. case lists:keyfind(K, 1, Ps) of {K,Kps} -> lists:keyreplace(K, 1, Ps, {K,F(Ks, Val, Kps, F)}); false -> Ps ++ [{K,F(Ks, Val, [], F)}] end end, %% Get the release number. %% We have stolen the idea and most of the code from rebar3. OTPRelease = fun () -> case erlang:system_info(otp_release) of [$R,N1|Rest] when is_integer(N1) -> %% If OTP <= R16, take the digits. [N1|Rest]; Rel -> %% If OTP >= 17.x, erlang:system_info(otp_release) returns %% just the major version number. File = filename:join([code:root_dir(),"releases",Rel,"OTP_VERSION"]), case file:read_file(File) of {error, _} -> Rel; {ok, Vsn} -> Size = byte_size(Vsn), %% The shortest vsn string consists of at least %% two digits followed by "\n". Therefore, it's %% safe to assume Size >= 3. case binary:part(Vsn, {Size, -3}) of <<"**\n">> -> binary:bin_to_list(Vsn, {0, Size - 3}); _ -> binary:bin_to_list(Vsn, {0, Size - 1}) end end end end, Version = OTPRelease(), %% Collect the macro definitions we will add to the compiler options. %% Named funs would be nicer but not always available. AppendCopts = fun (Version, [{Ver,Opt}|Opts], F) -> Rest = F(Version, Opts, F), if Version >= Ver -> [{d,Opt,true}|Rest]; true -> Rest end; (_Version, [], _F) -> [] end, Copts0 = [{d,'ERLANG_VERSION',Version}], Copts = Copts0 ++ AppendCopts(Version, [{"17",'HAS_MAPS'}, {"18",'HAS_FULL_KEYS'}, {"19",'NEW_REC_CORE'}, {"19",'NEW_RAND'}, {"20",'NEW_BOOL_GUARD'}, {"20",'HAS_FLOOR'}, {"20",'HAS_CEIL'}, {"21",'NEW_STACKTRACE'}, {"23",'EEP48'}, {"27",'OTP27_MAYBE'}], AppendCopts), %% Ensure they are in erl_opts. %% Make sure debug_info is always included for documentation AllOpts = [{debug_info, true} | Copts], Conf1 = case lists:keyfind(erl_opts, 1, Conf0) of {erl_opts,Opts} -> %Existing erl_opts %% Make sure debug_info is included in the final options OptsWithDebug = case lists:member(debug_info, Opts) of true -> lists:delete(debug_info, Opts) ++ [{debug_info, true}]; false -> Opts ++ [{debug_info, true}] end, NewOpts = {erl_opts, OptsWithDebug ++ Copts}, lists:keyreplace(erl_opts, 1, Conf0, NewOpts); false -> %No erl_opts Conf0 ++ [{erl_opts, AllOpts}] end, %% TestConfig = [{cover_enabled, true}, {cover_opts, [verbose]}], %% Aliases = [{alias, [ %% {test, [eunit, {ct, "--cover"}, cover]} %% ]}], %% Conf1 ++ TestConfig ++ Aliases. %% Conf1. rvirding-luerl-772b25e/examples/0000775000232200023220000000000015114130670017202 5ustar debalancedebalancervirding-luerl-772b25e/examples/benchmark/0000775000232200023220000000000015114130670021134 5ustar debalancedebalancervirding-luerl-772b25e/examples/benchmark/benchmarks.erl0000664000232200023220000000314115114130670023754 0ustar debalancedebalance%% File : benchmarks.erl %% Purpose : Benchmarks runner for luerl %% Use: erlc -I ../src benchmark.erl && erl -pa ../ebin -s benchmark run -s init stop -noshell %% Or: make -module(benchmarks). -export([run/0, run/1]). -export([benchmarks/1, do_benchmark/2, do_iteration/3]). -define(DEFAULT_ITER, 1000). run() -> Files = filelib:wildcard("suites/*.lua"), [ run(File) || File <- Files ] , ok. run(File) -> Lua0 = luerl:init(), {ok, _Resp, Lua2} = luerl:dofile(File, Lua0), report_file(File), [ do_benchmark(Benchmark, Lua2) || Benchmark <- benchmarks(Lua2) ], {ok, Lua2}. do_benchmark(Benchmark, Lua) -> Iter = num_iterations(Lua), report_benchmark(Benchmark), {Time, _Resp} = timer:tc(?MODULE, do_iteration, [Iter, Benchmark, Lua]), report_time(Time), ok. do_iteration(0, _Benchmark, _Lua) -> ok; do_iteration(Iter, Benchmark, Lua) -> luerl:call_method([<<"bench">>], Benchmark, [], Lua), do_iteration(Iter - 1, Benchmark, Lua). num_iterations(Lua) -> case luerl:do("return NUM_ITERATIONS", Lua) of {ok, [Iter], _} when is_number(Iter) -> round(Iter); _Any -> ?DEFAULT_ITER end. benchmarks(Lua0) -> io:format("benchmarks\n", []), {ok, [Benchmarks], _} = luerl:dofile_dec("util/extract_bench_keys.lua", Lua0), [ Key || {_Index, Key} <- Benchmarks ]. report_file(File) -> io:format("~n~s ms~n", [string:left(File, 26)]). report_benchmark(Benchmark) -> io:format(" ~s", [string:left(binary_to_list(Benchmark), 24, $.)]). report_time(Time) -> io:format(" ~p~n", [Time / 1000]). rvirding-luerl-772b25e/examples/benchmark/util/0000775000232200023220000000000015114130670022111 5ustar debalancedebalancervirding-luerl-772b25e/examples/benchmark/util/extract_bench_keys.lua0000664000232200023220000000013015114130670026452 0ustar debalancedebalancelocal list = {} for key, func in pairs(bench) do list[#list+1] = key end return list rvirding-luerl-772b25e/examples/benchmark/Makefile0000664000232200023220000000044615114130670022600 0ustar debalancedebalanceEXAMPLES = benchmarks ROOTDIR = ../.. SRCDIR = $(ROOTDIR)/src BEAMDIR = $(ROOTDIR)/ebin all: $(EXAMPLES) clean: rm -f *.beam erl_crash.dump .SECONDARY: %.beam: %.erl $(SRCDIR)/*.hrl erlc -I $(SRCDIR) $< %: %.beam erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell .PHONY: all clean rvirding-luerl-772b25e/examples/benchmark/suites/0000775000232200023220000000000015114130670022450 5ustar debalancedebalancervirding-luerl-772b25e/examples/benchmark/suites/chaincall.lua0000664000232200023220000000173515114130670025077 0ustar debalancedebalancebench = {} local function chain() return chain end local function plain() -- No-op end bench.chain_upval = function() chain () () () () () () () () () () -- 10 calls end bench.plain_upval = function() plain () plain () plain () plain () plain () plain () plain () plain () plain () plain () -- 10 calls end bench.plain_chain_upval = function() chain () chain () chain () chain () chain () chain () chain () chain () chain () chain () -- 10 calls end bench.chain_local = function() local chain = chain chain () () () () () () () () () () -- 10 calls end bench.plain_local = function() local plain = plain plain () plain () plain () plain () plain () plain () plain () plain () plain () plain () -- 10 calls end bench.plain_chain_local = function() local chain = chain chain () chain () chain () chain () chain () chain () chain () chain () chain () chain () -- 10 calls end return bench rvirding-luerl-772b25e/examples/benchmark/suites/return.lua0000664000232200023220000000063115114130670024472 0ustar debalancedebalancebench = {} local function no_ret() end local function ret_nil() return nil end local function ret_true() return true end local function ret_self() return ret_self end bench.no_ret = function() local a = no_ret() end bench.ret_nil = function() local a = ret_nil() end bench.ret_true = function() local a = ret_true() end bench.ret_self = function() local a = ret_self() end return bench rvirding-luerl-772b25e/examples/benchmark/suites/callmap1.lua0000664000232200023220000000142115114130670024643 0ustar debalancedebalancelocal noop = function() end local plain_call = function(a) noop() end local if_call = function(a) if a == "a1" then noop() elseif a == "a2" then noop() elseif a == "a3" then noop() elseif a == "a4" then noop() elseif a == "a5" then noop() elseif a == "a6" then noop() elseif a == "a7" then noop() elseif a == "a8" then noop() elseif a == "a9" then noop() elseif a == "a10" then noop() end end local map = { } for i = 1, 10 do map["a"..i] = noop end local map_call = function(a) map[a]() end local do_bench = function(fn) return function() fn("a"..1) end end bench = { noop = do_bench(noop); plain_call = do_bench(plain_call); if_call = do_bench(if_call); map_call = do_bench(map_call); } return bench rvirding-luerl-772b25e/examples/benchmark/suites/get.lua0000664000232200023220000000164415114130670023737 0ustar debalancedebalancelocal rawget = rawget local t = { true, a = true } local get = function(t, k) return t[k] end bench = { } bench.nonnil_num_plain = function() return t[1] end bench.nonnil_num_get = function() return get(t, 1) end bench.nonnil_num_rawget = function() return rawget(t, 1) end bench.nonnil_str_plain = function() return t["a"] end bench.nonnil_str_sugar = function() return t.a end bench.nonnil_str_get = function() return get(t, "a") end bench.nonnil_str_rawget = function() return rawget(t, "a") end bench.nil_num_plain = function() return t[2] end bench.nil_num_get = function() return get(t, 2) end bench.nil_num_rawget = function() return rawget(t, 2) end bench.nil_str_plain = function() return t["b"] end bench.nil_str_sugar = function() return t.b end bench.nil_str_get = function() return get(t, "b") end bench.nil_str_rawget = function() return rawget(t, "b") end return bench rvirding-luerl-772b25e/examples/benchmark/suites/mtvsclosure.lua0000664000232200023220000000106415114130670025542 0ustar debalancedebalancelocal setmetatable = setmetatable local mt = { __call = function(t, v) t[#t + 1] = v end } local call_setmetatable = function() return setmetatable({ }, mt) end local create_closure = function() local t = {} return function(v) t[#t + 1] = v end end local mt_obj = call_setmetatable() local fn_obj = create_closure() bench = { } bench.call_setmetatable = call_setmetatable bench.create_closure = create_closure -- bench.use_setmetatable = function() -- mt_obj("boo!") -- end bench.use_closure = function() fn_obj("boo!") end return bench rvirding-luerl-772b25e/examples/benchmark/suites/concat.lua0000664000232200023220000000175715114130670024434 0ustar debalancedebalancelocal table_concat, table_insert = table.concat, table.insert NUM_ITERATIONS = 1 bench = {} bench.raw_concat = function() local self = "string 0\n" for i = 1, 1000 do self = self .. "string " .. i .. "\n" end return self end bench.raw_plus_1 = function() local self = { "string 0\n" } for i = 1, 1000 do self[#self + 1] = "string " self[#self + 1] = i self[#self + 1] = "\n" end return table_concat(self) end bench.raw_insert = function() local self = { "string 0\n" } for i = 1, 1000 do table_insert(self, "string ") table_insert(self, i ) table_insert(self, "\n" ) end return table_concat(self) end bench.mixed_plus_1 = function() local self = {"string 0\n"} for i = 1,1000 do self[#self + 1] = "string " .. i .. "\n" end return table_concat(self) end bench.mixed_insert = function() local self = {"string 0\n"} for i = 1, 1000 do table_insert(self, "string " .. i .. "\n") end return table_concat(self) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/callmap10.lua0000664000232200023220000000146015114130670024726 0ustar debalancedebalancelocal noop = function() end local plain_call = function(a) noop() end local if_call = function(a) if a == "a1" then noop() elseif a == "a2" then noop() elseif a == "a3" then noop() elseif a == "a4" then noop() elseif a == "a5" then noop() elseif a == "a6" then noop() elseif a == "a7" then noop() elseif a == "a8" then noop() elseif a == "a9" then noop() elseif a == "a10" then noop() end end local map = { } for i = 1, 10 do map["a"..i] = noop end local map_call = function(a) map[a]() end local do_bench = function(fn) return function() for i = 1, 10 do fn("a"..i) end end end bench = { noop = do_bench(noop); plain_call = do_bench(plain_call); if_call = do_bench(if_call); map_call = do_bench(map_call); } return bench rvirding-luerl-772b25e/examples/benchmark/suites/nloop_simple.lua0000664000232200023220000000521615114130670025657 0ustar debalancedebalancelocal ipairs = ipairs local t5, t25, t50 = {}, {}, {} do for i = 1, 10 do t5[i] = i end t5[6] = nil for i = 1, 30 do t25[i] = i end t25[26] = nil for i = 1, 55 do t50[i] = i end t50[51] = nil end local do_nothing = function() end bench = {} local do_loop_ipairs = function(t) for i, v in ipairs(t) do end end local do_loop_numfor = function(t) for i = 1, #t do if t[i] == nil then break end end end local do_loop_while = function(t) local i = 1 while t[i] ~= nil do i = i + 1 end end bench.loop_ipairs_50 = function() do_loop_ipairs(t50) do_nothing(t50) -- Padding to get equivalent number of function calls. do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) end bench.loop_numfor_50 = function() do_loop_numfor(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) end bench.loop_while_50 = function() do_loop_while(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) do_nothing(t50) end bench.loop_ipairs_25 = function() do_loop_ipairs(t25) do_loop_ipairs(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) end bench.loop_numfor_25 = function() do_loop_numfor(t25) do_loop_numfor(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) end bench.loop_while_25 = function() do_loop_while(t25) do_loop_while(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) do_nothing(t25) end bench.loop_ipairs_5 = function() do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) do_loop_ipairs(t5) end bench.loop_numfor_5 = function() do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) do_loop_numfor(t5) end bench.loop_while_5 = function() do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) do_loop_while(t5) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/accum.lua0000664000232200023220000000516715114130670024254 0ustar debalancedebalancelocal assert, loadstring = assert, loadstring local pairs, ipairs, next = pairs, ipairs, next local table_concat = table.concat -------------------------------------------------------------------------------- local DATA = { 006.635; 009.210; 011.345; 013.277; 015.086; 016.812; 018.475; 020.090; 021.666; 023.209; 024.725; 026.217; 027.688; 029.141; 030.578; 032.000; 033.409; 034.805; 036.191; 037.566; 038.932; 040.289; 041.638; 042.980; 044.314; 045.642; 046.963; 048.278; 049.588; 050.892; 052.191; 053.486; 054.776; 056.061; 057.342; 058.619; 059.893; 061.162; 062.428; 063.691; 064.950; 066.206; 067.459; 068.710; 069.957; 071.201; 072.443; 073.683; 074.919; 076.154; 077.386; 078.616; 079.843; 081.069; 082.292; 083.513; 084.733; 085.950; 087.166; 088.379; 089.591; 090.802; 092.010; 093.217; 094.422; 095.626; 096.828; 098.028; 099.228; 100.425; 101.621; 102.816; 104.010; 105.202; 106.393; 107.583; 108.771; 109.958; 111.144; 112.329; 113.512; 114.695; 115.876; 117.057; 118.236; 119.414; 120.591; 121.767; 122.942; 124.116; 125.289; 126.462; 127.633; 128.803; 129.973; 131.141; 132.309; 133.476; 134.642; } local DATA_SIZE = #DATA -------------------------------------------------------------------------------- local accum_unrolled do local buf = { "return function(t, c) c = c or 0; " } for i = 1, DATA_SIZE do buf[#buf + 1] = "c = c + t["..i.."]; " end buf[#buf + 1] = "return c; end" local fn = assert(loadstring(table_concat(buf))) accum_unrolled = assert(fn()) end local accum_numeric_for = function(t, c) c = c or 0 for i = 1, #t do c = c + t[i] end return c end local accum_numeric_while = function(t, c) c = c or 0 local i = 1 local v = t[i] while v ~= nil do c = c + v i = i + 1 v = t[i] end return c end local accum_ipairs = function(t, c) c = c or 0 for _, v in ipairs(t) do c = c + v end return c end local accum_pairs = function(t, c) c = c or 0 for _, v in pairs(t) do c = c + v end return c end local accum_next = function(t, c) c = c or 0 local k, v = next(t) while k ~= nil do c = c + v k, v = next(t, k) end return c end -------------------------------------------------------------------------------- bench = { } bench.unrolled = function() return accum_unrolled(DATA) end bench.numeric_while = function() return accum_numeric_while(DATA) end bench.numeric_for = function() return accum_numeric_for(DATA) end bench.ipairs = function() return accum_ipairs(DATA) end bench.pairs = function() return accum_pairs(DATA) end bench.next = function() return accum_next(DATA) end return table.pack(bench) rvirding-luerl-772b25e/examples/benchmark/suites/str_is_empty.lua0000664000232200023220000000110215114130670025666 0ustar debalancedebalancelocal empty_string = "" bench = { } bench.noop = function() local a = "" return true end bench.empty_constant = function() local a = "" return a == "" end bench.empty_upvalue = function() local a = "" return a == empty_string end bench.empty_size = function() local a = "" return #a == 0 end bench.nonempty_constant = function() local a = "nonempty" return a == "" end bench.nonempty_upvalue = function() local a = "nonempty" return a == empty_string end bench.nonempty_size = function() local a = "nonempty" return #a == 0 end return bench rvirding-luerl-772b25e/examples/benchmark/suites/factory.lua0000664000232200023220000000622615114130670024630 0ustar debalancedebalancelocal pairs, setmetatable = pairs, setmetatable local clone_table = function(t) local r = {} for k, v in pairs(t) do r[k] = v end return r end local inplace = function() local factory do factory = function() return { method1 = function(self) end; method2 = function(self) end; method3 = function(self) end; method4 = function(self) end; method5 = function(self) end; method6 = function(self) end; method7 = function(self) end; method8 = function(self) end; method9 = function(self) end; method10 = function(self) end; } end end return factory end local plain = function() local factory do local method1 = function(self) end local method2 = function(self) end local method3 = function(self) end local method4 = function(self) end local method5 = function(self) end local method6 = function(self) end local method7 = function(self) end local method8 = function(self) end local method9 = function(self) end local method10 = function(self) end factory = function() return { method1 = method1; method2 = method2; method3 = method3; method4 = method4; method5 = method5; method6 = method6; method7 = method7; method8 = method8; method9 = method9; method10 = method10; } end end return factory end local mt = function() local factory do local mt = { __index = { method1 = function(self) end; method2 = function(self) end; method3 = function(self) end; method4 = function(self) end; method5 = function(self) end; method6 = function(self) end; method7 = function(self) end; method8 = function(self) end; method9 = function(self) end; method10 = function(self) end; }; } factory = function() return setmetatable({}, mt) end end return factory end local clone = function() local factory do local proto = { method1 = function(self) end; method2 = function(self) end; method3 = function(self) end; method4 = function(self) end; method5 = function(self) end; method6 = function(self) end; method7 = function(self) end; method8 = function(self) end; method9 = function(self) end; method10 = function(self) end; } factory = function() return clone_table(proto) end end return factory end local invoker = function(factory) local obj = factory() return function() obj:method1() obj:method2() obj:method3() obj:method4() obj:method5() obj:method6() obj:method7() obj:method8() obj:method9() obj:method10() end end bench = { inplace_init = inplace; plain_init = plain; metatable_init = mt; clone_init = clone; inplace_call = inplace(); plain_call = plain(); metatable_call = mt(); clone_call = clone(); inplace_method = invoker(inplace()); plain_method = invoker(plain()); metatable_method = invoker(mt()); clone_method = invoker(clone()); } return bench rvirding-luerl-772b25e/examples/benchmark/suites/arguments.lua0000664000232200023220000001635115114130670025166 0ustar debalancedebalancelocal select, tostring, assert, type, error = select, tostring, assert, type, error -------------------------------------------------------------------------------- local run_plain_assert = function(a, b, c) assert(type(a) == "number") assert(type(b) == "boolean") assert(type(c) == "string") end -------------------------------------------------------------------------------- local run_assert_is do local make_assert_is = function(typename) return function(v, msg) if type(v) == typename then return v end error( (msg or "assertion failed") .. ": expected `" .. typename .. "', got `" .. type(v) .. "'", 3 ) end end local assert_is_number = make_assert_is("number") local assert_is_boolean = make_assert_is("boolean") local assert_is_string = make_assert_is("string") run_assert_is = function(a, b, c) assert_is_number(a) assert_is_boolean(b) assert_is_string(c) end end -------------------------------------------------------------------------------- local run_arguments_select_simple do local arguments_select = function(...) local nargs = select("#", ...) for i = 1, nargs, 2 do local expected_type, value = select(i, ...) if type(value) ~= expected_type then error( "bad argument #" .. ((i + 1) / 2) .. " type: expected `" .. expected_type .. "', got `" .. type(value) .. "'", 3 ) end end end run_arguments_select_simple = function(a, b, c) arguments_select( "number", a, "boolean", b, "string", c ) end end -------------------------------------------------------------------------------- local run_arguments_recursive_simple do -- Simplified lua-nucleo version, equivalent to the others. local function impl(arg_n, expected_type, value, ...) -- Points error on function, calling function which calls *arguments() if type(value) ~= expected_type then error( "argument #"..arg_n..": expected `"..tostring(expected_type) .. "', got `"..type(value).."'", 3 + arg_n ) end -- If have at least one more type, check it return ((...) ~= nil) and impl(arg_n + 1, ...) or true end local arguments_recursive = function(...) local nargs = select('#', ...) return (nargs > 0) and impl(1, ...) or true end run_arguments_recursive_simple = function(a, b, c) arguments_recursive( "number", a, "boolean", b, "string", c ) end end -------------------------------------------------------------------------------- local run_arguments_recursive_lua_nucleo do -- Taken directly from lua-nucleo local lua51_types = { ["nil"] = true; ["boolean"] = true; ["number"] = true; ["string"] = true; ["table"] = true; ["function"] = true; ["thread"] = true; ["userdata"] = true; } local function impl(is_optional, arg_n, expected_type, value, ...) -- Points error on function, calling function which calls *arguments() if type(value) ~= expected_type then if not lua51_types[expected_type] then error( "argument #"..arg_n..": bad expected type `"..tostring(expected_type).."'", 3 + arg_n ) end if not is_optional or value ~= nil then error( (is_optional and "optional" or "") .. "argument #"..arg_n..": expected `"..tostring(expected_type) .. "', got `"..type(value).."'", 3 + arg_n ) end end -- If have at least one more type, check it return ((...) ~= nil) and impl(is_optional, arg_n + 1, ...) or true end local arguments_recursive = function(...) local nargs = select('#', ...) return (nargs > 0) and ( (nargs % 2 == 0) and impl(false, 1, ...) -- Not optional or error("arguments: bad call, dangling argument detected") ) or true end run_arguments_recursive_lua_nucleo = function(a, b, c) arguments_recursive( "number", a, "boolean", b, "string", c ) end end -------------------------------------------------------------------------------- -- TODO: Add a version with full-blown validation. local run_arguments_unroll_simple do -- TODO: Put a code-generation metatable over cache -- and pre-populate it for cases with (1-10) * 2 arguments. -- If __index sees odd number, it should crash -- with dangling argument error. local arguments_cache = { [6] = function(t1, v1, t2, v2, t3, v3) if type(v1) ~= t1 then error( "argument #1: expected `"..tostring(t1) .. "', got `"..type(v1).."'", 4 ) end if type(v2) ~= t2 then error( "argument #2: expected `"..tostring(t2) .. "', got `"..type(v2).."'", 4 ) end if type(v3) ~= t3 then error( "argument #3: expected `"..tostring(t3) .. "', got `"..type(v3).."'", 4 ) end end; } local arguments = function(...) local n = select("#", ...) -- Assuming cache is pre-populated for all possible use-cases return assert(arguments_cache[n])(...) end run_arguments_unroll_simple = function(a, b, c) arguments( "number", a, "boolean", b, "string", c ) end end -------------------------------------------------------------------------------- local run_arguments_hardcoded_simple do -- Not much real-word meaning, just for comparison with -- run_arguments_unroll_simple. local hardcoded_arguments_6 = function(t1, v1, t2, v2, t3, v3) if type(v1) ~= t1 then error( "argument #1: expected `"..tostring(t1) .. "', got `"..type(v1).."'", 2 ) end if type(v2) ~= t2 then error( "argument #2: expected `"..tostring(t2) .. "', got `"..type(v2).."'", 2 ) end if type(v3) ~= t3 then error( "argument #3: expected `"..tostring(t3) .. "', got `"..type(v3).."'", 2 ) end end run_arguments_hardcoded_simple = function(a, b, c) hardcoded_arguments_6( "number", a, "boolean", b, "string", c ) end end -------------------------------------------------------------------------------- bench = { } bench.plain_assert = function() run_plain_assert(42, true, "aaa") end bench.assert_is = function() run_assert_is(42, true, "aaa") end bench.assert_is_alloc = function() -- Imitating args table allocation. -- Needed to compensate plain Lua interpreter -- compatibility mode. local a, b, c = { }, { }, { } run_assert_is(42, true, "aaa") end bench.args_select_simple = function() run_arguments_select_simple(42, true, "aaa") end bench.args_recursive_simp = function() run_arguments_recursive_simple(42, true, "aaa") end bench.args_recursive_ln = function() run_arguments_recursive_lua_nucleo(42, true, "aaa") end bench.args_unroll_simple = function() run_arguments_unroll_simple(42, true, "aaa") end bench.args_hard_simple = function() run_arguments_hardcoded_simple(42, true, "aaa") end return bench rvirding-luerl-772b25e/examples/benchmark/suites/sort-simple.lua0000664000232200023220000000326615114130670025440 0ustar debalancedebalancelocal table_sort = table.sort local math_random, math_randomseed = math.random, math.randomseed -------------------------------------------------------------------------------- math_randomseed(12345) -------------------------------------------------------------------------------- -- TODO: Benchmark some pure-lua qsort local DATA_SIZE = 1e1 local generate_data = function() local t = { } for i = 1, DATA_SIZE do t[i] = math_random() end return t end local less = function(lhs, rhs) return lhs < rhs end local bubble_sort = function(t) for i = 2, #t do local switched = false for j = #t, i, -1 do if t[j] < t[j - 1] then t[j], t[j - 1] = t[j - 1], t[j] switched = true end end if switched == false then return t end end return t end local bubble_sort_cb = function(t, less) for i = 2, #t do local switched = false for j = #t, i, -1 do if less(t[j], t[j - 1]) then t[j], t[j - 1] = t[j - 1], t[j] switched = true end end if switched == false then return t end end return t end -------------------------------------------------------------------------------- bench = { } bench.generate_only = function() local data = generate_data() return true end bench.tsort_nocallback = function() local data = generate_data() return table_sort(data) end bench.tsort_callback = function() local data = generate_data() return table_sort(data, less) end bench.bubble_nocallback = function() local data = generate_data() return bubble_sort(data) end bench.bubble_callback = function() local data = generate_data() return bubble_sort_cb(data, less) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/vararg.lua0000664000232200023220000000223415114130670024436 0ustar debalancedebalancelocal noop = function() end local vararg_callback = function(...) end local call_noargs = function(fn) fn() end local call_vararg = function(fn, ...) fn(...) end local call_3 = function(fn, a, b, c) fn(a, b, c) end NUM_ITER = 1000 bench = { } bench.noop = noop bench.vararg_callback = vararg_callback bench.call_noargs_noop_nil = function() call_noargs(noop) end bench.call_noargs_vararg_nil = function() call_noargs(vararg_callback) end bench.call_vararg_noop_nil = function() call_noargs(noop) end bench.call_vararg_vararg_nil = function() call_noargs(vararg_callback) end bench.call_3_noop_nil = function() call_3(noop) end bench.call_3_vararg_nil = function() call_3(vararg_callback) end bench.call_noargs_noop_3 = function() call_noargs(noop, 1, 2, 3) end bench.call_noargs_vararg_3 = function() call_noargs(vararg_callback, 1, 2, 3) end bench.call_vararg_noop_3 = function() call_noargs(noop, 1, 2, 3) end bench.call_vararg_vararg_3 = function() call_noargs(vararg_callback, 1, 2, 3) end bench.call_3_noop_3 = function() call_3(noop, 1, 2, 3) end bench.call_3_vararg_3 = function() call_3(vararg_callback, 1, 2, 3) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/elseif_large.lua0000664000232200023220000000265615114130670025605 0ustar debalancedebalance-- TODO: ?! Is this benchmark still relevant? local tostring, assert, loadstring, ipairs = tostring, assert, loadstring, ipairs local table_concat = table.concat local math_floor = math.floor local noop = function() end local plain_call = function(a) noop() end local make_plain_call = function() return plain_call end local make_elseifs = function(n) local buf = { } local _ = function(v) buf[#buf + 1] = tostring(v) end _ "local noop = function() end " _ "return function(a)" _ " if a == 'a" _(1) _"' then noop()" for i = 2, n do _ " elseif a == 'a" _(i) _"' then noop()" end _ " else error('unknown param') end" _ " end" return assert(loadstring(table_concat(buf)))() end local make_callmaps = function(n) local buf = { } local _ = function(v) buf[#buf + 1] = tostring(v) end _ "local noop = function() end " _ "local map = {" for i = 1, n do _ "a" _(i) _ " = noop;" end _ "} " _ "return function(a) assert(map[a])() end" return assert(loadstring(table_concat(buf)))() end local bench_fn = function(fn, n) return function() fn("a" .. math_floor(n / 2 + 0.5)) end end local mark_fn = function(make, n) return bench_fn(make(n), n) end bench = { } for _, i in ipairs { 1, 5, 10, 15, 20, 100, 250, 500, 1000 } do bench["plain_"..i] = mark_fn(make_plain_call, i) bench["callmap_"..i] = mark_fn(make_callmaps, i) bench["elseif_"..i] = mark_fn(make_elseifs, i) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/tailcall.lua0000664000232200023220000000064715114130670024747 0ustar debalancedebalancelocal ret = function(t) t[1] = true return t end local noret = function(t) t[1] = true end bench = { } bench.tailcall_local = function() local t = {} return ret(t) end bench.tailcall_nolocal = function() return ret({}) end bench.notailcall_return = function() local t = {} ret(t) return t end bench.notailcall_noreturn = function() local t = {} noret(t) return t end return table.pack(bench) rvirding-luerl-772b25e/examples/benchmark/suites/nloop.lua0000664000232200023220000000120015114130670024273 0ustar debalancedebalancelocal ipairs = ipairs local t = {} for i = 1, 55 do t[i] = i end t[51] = nil -- A hole bench = {} -- Bench function does: -- 1. Set all table elements to 0 until the first hole. -- 2. Return hole position. bench.loop_ipairs = function() local j for i, v in ipairs(t) do t[i] = 0 j = i end return j + 1 end bench.loop_for = function() local n = #t local j = n for i = 1, n do local v = t[i] if v == nil then j = i break end t[i] = 0 end return j end bench.loop_while = function() local i = 1 while t[i] ~= nil do t[i] = 0 i = i + 1 end return i end return bench rvirding-luerl-772b25e/examples/benchmark/suites/inf.lua0000664000232200023220000000047615114130670023736 0ustar debalancedebalancelocal tonumber = tonumber local math_huge = math.huge bench = {} -- bench.e309 = function() -- local inf = 1e309 -- end bench.huge = function() local inf = math_huge end -- bench.divide = function() -- local inf = 1/0 -- end -- bench.tonumber = function() -- local inf = tonumber("inf") -- end return bench rvirding-luerl-772b25e/examples/benchmark/suites/COPYRIGHT0000664000232200023220000000264615114130670023753 0ustar debalancedebalanceLuamarca is licensed under the terms of the MIT license reproduced below. This means that Luamarca is free software and can be used for both academic and commercial purposes at absolutely no cost. =============================================================================== Copyright (C) 2008-2009 Luamarca authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =============================================================================== (end of COPYRIGHT) rvirding-luerl-772b25e/examples/benchmark/suites/selectvstable.lua0000664000232200023220000000106115114130670026011 0ustar debalancedebalancelocal select = select local select_test = function(...) local nargs = select("#", ...) local r = { } for i = 1, nargs do r[#r + 1] = select(i, ...) * 2 end return r end local table_test = function(...) local nargs = select("#", ...) -- Still have to do this in case of nils local args = { ... } local r = { } for i = 1, nargs do r[#r + 1] = args[i] * 2 end return r end bench = { } bench.select = function() return select_test(3, 5, 1, 9, 7) end bench.table = function() return table_test(3, 5, 1, 9, 7) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/tclone.lua0000664000232200023220000001455415114130670024450 0ustar debalancedebalance-------------------------------------------------------------------------------- -- tclone.lua: tclone benchmark -------------------------------------------------------------------------------- local type, pairs, assert, error, unpack, next, newproxy = type, pairs, assert, error, unpack, next, newproxy local math_randomseed, math_random = math.randomseed, math.random local string_char = string.char local table_concat = table.concat -- local coroutine_create = coroutine.create -------------------------------------------------------------------------------- math_randomseed(123456) -------------------------------------------------------------------------------- -- -- Current lua-nucleo version (a166af) -- local tclone_nucleo do local function impl(t, visited) local t_type = type(t) if t_type ~= "table" then return t end assert(not visited[t], "recursion detected") visited[t] = true local r = { } for k, v in pairs(t) do r[impl(k, visited)] = impl(v, visited) end visited[t] = nil return r end tclone_nucleo = function(t) return impl(t, { }) end end -- -- tclone2 by Dimiter "malkia" Stanev -- http://article.gmane.org/gmane.comp.lang.lua.general/82378 -- local tclone2 do local function impl(t, visited, rtimes) local t_type = type(t) if t_type ~= "table" then return t end -- Don't remember all visited[t] levels -- Just remember every once in a 128 times -- If there is a recursion it'll still be detected -- But 128 stack levels deeper assert(not visited[t], "recursion detected (with some latency)") if rtimes == 128 then rtimes = 1 visited[t] = t end local r = { } for k, v in pairs(t) do r[impl(k, visited, rtimes + 1)] = impl(v, visited, rtimes + 1) end if rtimes == 1 then visited[t] = nil end return r end tclone2 = function(t) return impl(t, { }, 1) end end -- -- tclone5 by Dimiter "malkia" Stanev -- http://article.gmane.org/gmane.comp.lang.lua.general/82379 -- local tclone5 do local function impl(t, visited, rtimes) if visited[t] then error("recursion detected") end if rtimes == 128 then rtimes = 1 visited[t] = true end local r = { } for k, v in pairs(t) do if type(k) == "table" then if type(v) == "table" then r[impl(k, visited, rtimes + 1)] = impl(v, visited, rtimes + 1) else r[impl(k, visited, rtimes + 1)] = v end elseif type(v) == "table" then r[k] = impl(v, visited, rtimes + 1) else r[k] = v end end if rtimes == 1 then visited[t] = nil end return r end tclone5 = function(t) if type(t) == "table" then return impl(t, { }, 1) end return t end end -- -- tclone6 by Dimiter "malkia" Stanev -- http://article.gmane.org/gmane.comp.lang.lua.general/82601 -- With a fix in while condition -- local tclone6 do local function impl(t, visited, rtimes) if visited[t] then error("recursion detected") end if rtimes == 128 then rtimes = 1 visited[t] = true end local r = { } local k, v = next(t) while k ~= nil do if type(k) == "table" then if type(v) == "table" then r[impl(k, visited, rtimes + 1)] = impl(v, visited, rtimes + 1) else r[impl(k, visited, rtimes + 1)] = v end elseif type(v) == "table" then r[k] = impl(v, visited, rtimes + 1) else r[k] = v end k, v = next(t, k) end if rtimes == 1 then visited[t] = nil end return r end tclone6 = function(t) if type(t) == "table" then return impl(t, { }, 1) end return t end end -------------------------------------------------------------------------------- -- TODO: From lua-nucleo/test/table.lua. -- Make that available to other projects and reuse. local function gen_random_dataset(num, nesting, visited, random) random = random or math_random nesting = nesting or 1 visited = visited or {} num = num or random(0, 10) local gen_str = function() local len = random(1, 64) local t = {} for i = 1, len do t[i] = string_char(random(0, 255)) end return table_concat(t) end local gen_bool = function() return random() >= 0.5 end local gen_udata = function() return newproxy() end local gen_func = function() return function() end end -- local gen_thread = function() return coroutine_create(function() end) end local gen_nil = function() return nil end local gen_visited_link = function() if #visited > 1 then return visited[random(1, #visited)] else return gen_str() end end local generators = { gen_bool; gen_bool; gen_bool; function() return random(-10, 10) end; gen_str; gen_str; gen_str; --[[ gen_thread; gen_thread; gen_func; gen_func; gen_udata; gen_udata;--]] --gen_visited_link; function() if nesting >= 10 then return nil end local t = { } visited[#visited + 1] = t local n = random(0, 10 - nesting) for i = 1, n do local k = gen_random_dataset(1, nesting + 1, visited, random) if k == nil then k = "(nil)" end t[k] = gen_random_dataset(1, nesting + 1, visited, random) end return t end } local t = {} visited[#visited + 1] = t for i = 1, num do local n = random(1, #generators) t[i] = generators[n]() end return unpack(t, 1, num) end -------------------------------------------------------------------------------- local DATA = { gen_random_dataset(20) } -------------------------------------------------------------------------------- bench = { } -------------------------------------------------------------------------------- bench.lua_nucleo = function() local data = tclone_nucleo(DATA) assert(data ~= DATA) -- TODO: Check equality. end bench.tclone2 = function() local data = tclone2(DATA) assert(data ~= DATA) -- TODO: Check equality. end bench.tclone5 = function() local data = tclone5(DATA) assert(data ~= DATA) -- TODO: Check equality. end bench.tclone6 = function() local data = tclone5(DATA) assert(data ~= DATA) -- TODO: Check equality. end -------------------------------------------------------------------------------- return bench rvirding-luerl-772b25e/examples/benchmark/suites/is_integer.lua0000664000232200023220000000300515114130670025301 0ustar debalancedebalancelocal math_floor = math.floor bench = {} local integer, noninteger = 12345, 12345.67 local noop = function(a) return a end local isint_floor = function(a) if a == math_floor(a) then return true end return false end local isint_floor_direct = function(a) return (a == math_floor(a)) end local isint_mod = function(a) if a % 1 == 0 then return true end return false end local isint_mod_direct = function(a) return (a % 1 == 0) end local isint_bits = function(a) if (a + 2^52) - 2^52 == a then return true end return false end local isint_bits_direct = function(a) return (a + 2^52) - 2^52 == a end bench.noop_int = function() noop(integer) end bench.noop_nonint = function() noop(noninteger) end bench.floor_int = function() isint_floor(integer) end bench.floor_nonint = function() isint_floor(noninteger) end bench.floor_int_direct = function() isint_floor_direct(integer) end bench.floor_nonint_direct = function() isint_floor_direct(noninteger) end bench.mod_int = function() isint_mod(integer) end bench.mod_nonint = function() isint_mod(noninteger) end bench.mod_int_direct = function() isint_mod_direct(integer) end bench.mod_nonint_direct = function() isint_mod_direct(noninteger) end bench.bits_int = function() isint_bits(integer) end bench.bits_nonint = function() isint_bits(noninteger) end bench.bits_int_direct = function() isint_bits_direct(integer) end bench.bits_nonint_direct = function() isint_bits_direct(noninteger) end return bench rvirding-luerl-772b25e/examples/benchmark/suites/next_vs_pairs.lua0000664000232200023220000000057215114130670026043 0ustar debalancedebalancelocal pairs, next = pairs, next local t = {} for i = 1, 50 do t[i] = i -- Array part t[i * 256] = i -- Hash part end bench = {} bench.pairs = function() local sum = 0 for k, v in pairs(t) do sum = sum + v end end bench.next = function() local sum = 0 local k, v = next(t) while k ~= nil do sum = sum + v k, v = next(t, k) end end return bench rvirding-luerl-772b25e/examples/minibench/0000775000232200023220000000000015114130670021136 5ustar debalancedebalancervirding-luerl-772b25e/examples/minibench/minibench2.erl0000664000232200023220000001463715114130670023673 0ustar debalancedebalance%% File : minibench2.erl %% Author : Henning Diedrich %% File : luerl/examples/minibench/minibench.erl %% Purpose : Benchmark for frequent calls to small Luerl scripts %% Author : Henning Diedrich %% Use $ cd ./examples/minibench %% $ erlc minibench.erl %% $ erl -pa ../../ebin -s minibench run -s init stop -noshell %% Or $ make minibench -module(minibench2). -export([run/0]). run() -> io:format("----------------------------------------------------------~n"), io:format("This is a benchmark of frequent fast calls into Luerl.~n"), % I. eval and execute DoStr1 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Init state, parse and execute '~s'~n", [DoStr1]), I1 = 10000, {T1,_State} = timer:tc(fun() -> do_loop(I1, DoStr1) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T1,I1,DoStr1]), io:format("Per call: ~p microseconds.~n", [T1/I1]), % II. eval once, then only execute DoStr2 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Init state, and execute pre-parsed '~s'~n", [DoStr2]), I2 = 10000, {ok, Chunk2, State2} = luerl:load(DoStr2, luerl:init()), {T2,_State21} = timer:tc(fun() -> do_loop_state(I2, Chunk2, State2) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T2,I2,DoStr2]), io:format("Per call: ~p microseconds.~n", [T2/I2]), % III. eval once, then only execute DoStr3 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Execute pre-parse execute '~s', re-using same state~n", [DoStr3]), I3 = 10000, {ok, Chunk3, State3} = luerl:load(DoStr3, luerl:init()), {T3,_State31} = timer:tc(fun() -> do_loop_state(I3, Chunk3, State3) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T3,I3,DoStr3]), io:format("Per call: ~p microseconds.~n", [T3/I3]), % IV. measure but state initialization io:format("----------------------------------------------------------~n"), io:format("Pure initialization of Lua state~n"), I4 = 10000, {T4,_State41} = timer:tc(fun() -> [luerl:init() || _ <- lists:seq(1,I4)] end), io:format("Adding Up: ~p microseconds for ~p x initializing a Lua state.~n", [T4,I4]), io:format("Per call: ~p microseconds.~n", [T4/I4]), % V. eval once, then only execute, re-use previous state DoStr5 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Execute pre-parsed '~s', re-using state from last result~n", [DoStr5]), I5 = 10000, {ok, Chunk5, State5} = luerl:load(DoStr5, luerl:init()), {T5,_State51} = timer:tc(fun() -> do_loop_chain(I5, Chunk5, State5) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T5,I5,DoStr5]), io:format("Per call: ~p microseconds.~n", [T5/I5]), % Vb. function call, re-use previous state DoStr5b = "function OneAndOne() a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c end", io:format("----------------------------------------------------------~n"), io:format("Execute pre-parsed function '~s', re-using state from last result~n", [DoStr5b]), I5b = 10000, State5b = luerl:init(), {ok,[],State5b1} = luerl:do(DoStr5b, State5b), io:format("-"), {T5b,_State5b1} = timer:tc(fun() -> do_loop_do(I5b, "return OneAndOne()", State5b1) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T5b,I5b,DoStr5b]), io:format("Per call: ~p microseconds.~n", [T5b/I5b]), % Vc. empty function call, re-use previous state DoStr5c = "function EmptyFunc() end", io:format("----------------------------------------------------------~n"), io:format("Execute empty function, re-using state from last result~n"), I5c = 10000, State5c = luerl:init(), {ok,[],State5c1} = luerl:do(DoStr5c, State5c), io:format("-"), {T5c,_State5c1} = timer:tc(fun() -> do_loop_do(I5c, "EmptyFunc()", State5c1) end), io:format("Adding Up: ~p microseconds for ~p x calling empty function.~n", [T5c,I5c]), io:format("Per call: ~p microseconds.~n", [T5c/I5c]), % VI. measure but parsing DoStr6 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Pure parsing~n"), I6 = 10000, {T6,_State61} = timer:tc(fun() -> [luerl:load(DoStr6, luerl:init()) || _ <- lists:seq(1,I6)] end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T6,I6,DoStr6]), io:format("Per call: ~p microseconds.~n", [T6/I6]), % VII. Parse and execute DoStr7 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", io:format("----------------------------------------------------------~n"), io:format("Parse and execute 'a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b', re-using state~n"), I7 = 10000, State7 = luerl:init(), {T7,_State71} = timer:tc(fun() -> do_loop_do(I7, DoStr7, State7) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T7,I7,DoStr7]), io:format("Per call: ~p microseconds.~n", [T7/I7]), done. % helper do_loop(N, Chunk) when N > 0 -> luerl:do(Chunk, luerl:init()), do_loop(N-1, Chunk); do_loop(0, _) -> ok. do_loop_state(N, Chunk, State) when N > 0 -> luerl:call_chunk(Chunk, [], State), do_loop_state(N-1, Chunk, State); do_loop_state(0, _, _) -> ok. do_loop_do(N, String, State) when N > 0 -> {ok,_,State1} = luerl:do(String, State), do_loop_do(N-1, String, State1); do_loop_do(0, _, _) -> ok. do_loop_chain(N, Chunk, State0) when N > 0 -> {ok,_,State1} = luerl:call_chunk(Chunk, State0), do_loop_chain(N-1, Chunk, State1); do_loop_chain(0, _, _) -> ok. rvirding-luerl-772b25e/examples/minibench/minibench.erl0000664000232200023220000001112015114130670023571 0ustar debalancedebalance%% File : mini.erl %% Author : Henning Diedrich %% File : luerl/examples/minibench/minibench.erl %% Purpose : Benchmark for frequent calls to small Luerl scripts %% Author : Henning Diedrich %% Use $ cd ./examples/minibench %% $ erlc minibench.erl %% $ erl -pa ../../ebin -s minibench run -s init stop -noshell %% Or $ make minibench -module(minibench). -export([run/0]). run() -> io:format("----------------------------------------------------------~n"), io:format("This is a benchmark of frequent fast calls into Luerl.~n"), % I. eval and execute io:format("----------------------------------------------------------~n"), io:format("Init state, parse and execute '1 + 1'~n"), I1 = 100000, {T1,_State} = timer:tc(fun() -> do_loop(I1, "return 1 + 1") end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T1,I1]), io:format("Per call: ~p microseconds.~n", [T1/I1]), % II. eval once, then only execute io:format("----------------------------------------------------------~n"), io:format("Init state, and execute pre-parsed '1 + 1'~n"), I2 = 100000, {ok, Chunk2, State2} = luerl:load("return 1 + 1", luerl:init()), {T2,_State21} = timer:tc(fun() -> do_loop_state(I2, Chunk2, State2) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T2,I2]), io:format("Per call: ~p microseconds.~n", [T2/I2]), % III. eval once, then only execute io:format("----------------------------------------------------------~n"), io:format("Execute pre-parse execute '1 + 1', re-using same state~n"), I3 = 100000, State3 = luerl:init(), {ok, Chunk3, State31} = luerl:load("return 1 + 1", State3), {T3,_State31} = timer:tc(fun() -> do_loop_state(I3, Chunk3, State31) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T3,I3]), io:format("Per call: ~p microseconds.~n", [T3/I3]), % IV. measure but state initialization io:format("----------------------------------------------------------~n"), io:format("Pure initialization of Lua state~n"), I4 = 100000, {T4,_State41} = timer:tc(fun() -> [luerl:init() || _ <- lists:seq(1,I4)] end), io:format("Adding Up: ~p microseconds for ~p x initializing Lua state.~n", [T4,I4]), io:format("Per call: ~p microseconds.~n", [T4/I4]), % V. eval once, then only execute, re-use previous state io:format("----------------------------------------------------------~n"), io:format("Execute pre-parsed '1 + 1', re-using state from last result~n"), I5 = 100000, State5 = luerl:init(), {ok, Chunk5, State51} = luerl:load("return 1 + 1", State5), {T5,_State51} = timer:tc(fun() -> do_loop_chain(I5, Chunk5, State51) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T5,I5]), io:format("Per call: ~p microseconds.~n", [T5/I5]), % VI. measure but parsing io:format("----------------------------------------------------------~n"), io:format("Pure parsing~n"), I6 = 100000, {T6,_State61} = timer:tc(fun() -> [luerl:load("return 1 + 1", luerl:init()) || _ <- lists:seq(1,I6)] end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T6,I6]), io:format("Per call: ~p microseconds.~n", [T6/I6]), % VII. Parse and execute io:format("----------------------------------------------------------~n"), io:format("Parse and execute '1 + 1', re-using state~n"), I7 = 100000, State7 = luerl:init(), {T7,_State71} = timer:tc(fun() -> do_loop_do(I7, "return 1 + 1", State7) end), io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T7,I7]), io:format("Per call: ~p microseconds.~n", [T7/I7]), done. % helper do_loop(N, Chunk) when N > 0 -> luerl:do(Chunk, luerl:init()), do_loop(N-1, Chunk); do_loop(0, _) -> ok. do_loop_state(N, Chunk, State) when N > 0 -> luerl:call_chunk(Chunk, [], State), do_loop_state(N-1, Chunk, State); do_loop_state(0, _, _) -> ok. do_loop_do(N, String, State) when N > 0 -> luerl:do(String, State), do_loop_do(N-1, String, State); do_loop_do(0, _, _) -> ok. do_loop_chain(N, Chunk, State0) when N > 0 -> {ok,_,State1} = luerl:call_chunk(Chunk, [], State0), do_loop_chain(N-1, Chunk, State1); do_loop_chain(0, _, _) -> ok. rvirding-luerl-772b25e/examples/minibench/Makefile0000664000232200023220000000046515114130670022603 0ustar debalancedebalanceEXAMPLES = minibench \ minibench2 ROOTDIR = ../.. SRCDIR = $(ROOTDIR)/src BEAMDIR = $(ROOTDIR)/ebin all: $(EXAMPLES) clean: rm -f *.beam erl_crash.dump .SECONDARY: %.beam: %.erl $(SRCDIR)/*.hrl erlc -I $(SRCDIR) $< %: %.beam erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell .PHONY: all clean rvirding-luerl-772b25e/examples/Makefile0000664000232200023220000000024215114130670020640 0ustar debalancedebalanceSUBDIRS ?= hello all clean: @for subdir in $(SUBDIRS); do \ echo $(MAKE) -C $$subdir $@; \ $(MAKE) -C $$subdir $@; \ done .PHONY: all clean rvirding-luerl-772b25e/examples/hello/0000775000232200023220000000000015114130670020305 5ustar debalancedebalancervirding-luerl-772b25e/examples/hello/hello2-6.lua0000664000232200023220000000025315114130670022340 0ustar debalancedebalance-- File : hello2-6.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl a = 'new contents of a' print('(27) (a) ' .. a) return arvirding-luerl-772b25e/examples/hello/hello_table.erl0000664000232200023220000000210415114130670023260 0ustar debalancedebalance%% File : hello_table.erl %% Purpose : Brief demonstration of Luerl table access. %% Use $ erlc hello_table_new.erl && erl -pa ../../ebin -s hello_table_new run -s init stop -noshell -module(hello_table). -export([run/0]). run() -> LuaScript = <<"hello_table = { hello=\"world\" }; return hello_table">>, {ok, [_Table], Lua0} = luerl:do(LuaScript, luerl:init()), {ok,World,Lua1} = luerl:get_table_keys_dec([hello_table, hello], Lua0), {ok,Lua2} = luerl:set_table_keys_dec([hello_table, hello], there, Lua1), {ok,HelloDict,Lua3} = luerl:get_table_keys_dec([hello_table], Lua2), {ok,There, Lua4} = luerl:get_table_keys_dec([hello_table, hello], Lua3), io:format("(1) hello ~s ~s - ~p~n", [There, World, HelloDict]), {ok,Lua5} = luerl:set_table_keys([<<"hello_table">>, <<"goodbye">>], <<"bye">>, Lua4), {ok, Bye, Lua6} = luerl:get_table_keys([<<"hello_table">>, <<"goodbye">>], Lua5), {ok, HelloTab, _Lua7} = luerl:get_table_keys([<<"hello_table">>], Lua6), io:format("(2) ~s - ~p~n", [Bye, HelloTab]), done. rvirding-luerl-772b25e/examples/hello/hello_userdata.erl0000664000232200023220000000151715114130670024010 0ustar debalancedebalance%% File : hello_userdata.erl %% Purpose : Brief demonstration of Luerl userdata access. %% Use $ erlc hello_userdata_new.erl && erl -pa ../../ebin -s hello_userdata_new run -s init stop -noshell -module(hello_userdata). -export([run/0]). run() -> St0 = luerl:init(), U42 = {userdata,42}, %The original decoded data {Uref,St1} = luerl:encode(U42, St0), {ok,St2} = luerl:set_table_keys([<<"u1">>], Uref, St1), {ok,St3} = luerl:set_table_keys([<<"u2">>], Uref, St2), %% This call wraps the actual data for us. St4 = luerl_heap:set_userdata_data(Uref, 84, St3), {ok,Uref,St5} = luerl:get_table_keys([<<"u1">>], St4), {ok,Uref,St6} = luerl:get_table_keys([<<"u2">>], St5), U84 = {userdata,84}, %New decoded data U84 = luerl:decode(Uref, St6), St6. rvirding-luerl-772b25e/examples/hello/hello2-3.lua0000664000232200023220000000031215114130670022331 0ustar debalancedebalance-- File : hello2-3.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl function no() print("(16) No!") end print("(15) Maybe ...") return "(X) Yes!"rvirding-luerl-772b25e/examples/hello/hello_funcalls.erl0000664000232200023220000000234015114130670024002 0ustar debalancedebalance%% File : hello_funcalls.erl %% Purpose : Demonstration of various ways to expose Erlang functions to Luerl. %% Use $ erlc hello_funcalls.erl && erl -pa ../../ebin -s hello_funcalls run -s init stop -noshell -module(hello_funcalls). -include("luerl.hrl"). -export([run/0,mfa_function/3]). regular_function(Args, St) -> io:format("regular_function(~p)\n", [Args]), {[42], St}. mfa_function(StaticArgs, DynamicArgs, St) -> io:format("mfa_function(~p, ~p)\n", [StaticArgs, DynamicArgs]), {[42], St}. run() -> LuaScript = <<"return hello_funcall(4, 5, 6)">>, Lua = luerl:init(), {ok,Lua1} = luerl:set_table_keys_dec([<<"hello_funcall">>], fun regular_function/2, Lua), % The argument part of {M,F,A} won't get encoded, so that is why % we can get away with passing a tuple here. {ok,Lua2} = luerl:set_table_keys_dec([<<"hello_funcall">>], {hello_funcalls,mfa_function,{1,2}}, Lua), {ok,Res1, _} = luerl:do(LuaScript, Lua1), io:format("regular_function got ~p~n", Res1), {ok,Res2, _} = luerl:do(LuaScript, Lua2), io:format("mfa_function got ~p~n", Res2). rvirding-luerl-772b25e/examples/hello/hello2-5.lua0000664000232200023220000000024015114130670022333 0ustar debalancedebalance-- File : hello2-5.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl print ("(26) hello2-5.lua talking. a: " .. a)rvirding-luerl-772b25e/examples/hello/hello_sandbox.erl0000664000232200023220000000416515114130670023640 0ustar debalancedebalance%% File : hello_sandbox.erl %% Purpose : Brief demonstration of Luerl sandbox basics. %% Use $ erlc hello_sandbox.erl && erl -pa ./ebin -s hello_sandbox run -s init stop -noshell %% Or $ make hello_sandbox -module(hello_sandbox). -export([run/0]). run() -> %% Default sandboxed state. SbSt = luerl_sandbox:init(), io:format("inited\n"), %% Sandboxing globals %% {error, {lua_error, Reason, _}} = {lua_error, Reason, _} = luerl_sandbox:run("return os.getenv(\"HOME\")", [], SbSt), io:format("os.getenv with sandbox: ~p~n",[Reason]), %% Customizing sandbox %% first with default Luerl state. {ok,[<<"number">>], _} = luerl_sandbox:run("return type(1)", luerl:init()), %% then with sandboxed type function {lua_error, _, _} = luerl_sandbox:run("return type(1)", luerl_sandbox:init([['_G', type]])), %% Using sandboxed state outside of runner try luerl:do("return os.getenv(\"HOME\")", SbSt) catch _:_ -> io:format("catch error with os.getenv(\"HOME\") with sandbox~n", []) end, %% Setting values. MaxReductions = 100, SpawnOpts = [{priority, low}], Timeout = 1000, %% Script runner with reduction counting. Flags0 = #{max_reductions => MaxReductions}, {error, {reductions, R0}} = luerl_sandbox:run("a={}; for i=1,1000000 do a[i] = 5 end", Flags0, SbSt), io:format("killed process with reductions ~p > 100~n",[R0]), %% Sandboxed run with default Luerl state. Flags1 = #{max_reductions => MaxReductions, spawn_opts => SpawnOpts, max_time => Timeout}, {error, {reductions, R1}} = luerl_sandbox:run("x = 'a'; while true do x = x .. x end", Flags1, luerl:init()), io:format("killed process with reductions ~p > 100~n",[R1]), %% Unlimited reductions Flags3 = #{max_reductions => none}, {ok, [], _} = luerl_sandbox:run("a={}; for i=1,10 do a[i] = 5 end", Flags3, SbSt), io:format("Finished running with unlimited reductions ~n",[]), done. rvirding-luerl-772b25e/examples/hello/hello2-9.lua0000664000232200023220000000027415114130670022346 0ustar debalancedebalance-- File : hello2-9.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl function confirm(p) return p .. ' (it really is)' end return confirm(a)rvirding-luerl-772b25e/examples/hello/Makefile0000664000232200023220000000057515114130670021754 0ustar debalancedebalanceEXAMPLES = hello \ hello2 \ hello_table \ hello_userdata \ hello_sandbox \ hello_funcalls ROOTDIR = ../.. SRCDIR = $(ROOTDIR)/src BEAMDIR = $(ROOTDIR)/ebin all: $(EXAMPLES) clean: rm -f *.beam erl_crash.dump .SECONDARY: %.beam: %.erl $(SRCDIR)/*.hrl erlc -I $(SRCDIR) $< %: %.beam erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell .PHONY: all clean rvirding-luerl-772b25e/examples/hello/hello.erl0000664000232200023220000000111015114130670022105 0ustar debalancedebalance%% File : hello.erl %% Purpose : Brief demonstration of Luerl basics. %% Use $ erlc hello.erl && erl -pa ./ebin -s hello run -s init stop -noshell %% Or $ make hello -module(hello). -export([run/0]). run() -> % execute a string luerl:do("print(\"Hello, Robert(o)!\")", luerl:init()), % execute a file luerl:dofile("./hello.lua", luerl:init()), % separately parse, then execute State0 = luerl:init(), {ok, Chunk, State1} = luerl:load("print(\"Hello, Chunk!\")", State0), {ok,_Ret, _NewState} = luerl:call(Chunk, [], State1), done. rvirding-luerl-772b25e/examples/hello/hello2-2.lua0000664000232200023220000000020315114130670022327 0ustar debalancedebalance-- File : hello2-2.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl return 2137 * 42rvirding-luerl-772b25e/examples/hello/hello2-4.lua0000664000232200023220000000023215114130670022333 0ustar debalancedebalance-- File : hello2-4.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl return "'(18b) Evidently, Mr. Watson.'"rvirding-luerl-772b25e/examples/hello/hello2-8.lua0000664000232200023220000000025215114130670022341 0ustar debalancedebalance-- File : hello2-8.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl function old() print "(33) old" end print "(32) News!"rvirding-luerl-772b25e/examples/hello/hello2-7.lua0000664000232200023220000000027015114130670022340 0ustar debalancedebalance-- File : hello2-7.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl a = "(28a) οἶδα οὐκ εἰδώς, oîda ouk eidōs" return a rvirding-luerl-772b25e/examples/hello/hello.lua0000664000232200023220000000021615114130670022112 0ustar debalancedebalance-- File : hello.lua -- Purpose : Brief demonstration of Luerl basics - execution of a file. -- See : ./hello.erl print("Hello, File!")rvirding-luerl-772b25e/examples/hello/hello2-10.lua0000664000232200023220000000021215114130670022406 0ustar debalancedebalance-- File : hello2-10.lua -- Purpose : Returning lua dicts -- See : ./examples/hello/hello2.erl return {1,2,{3,'Hello World!'}} rvirding-luerl-772b25e/examples/hello/hello2-1.lua0000664000232200023220000000022715114130670022334 0ustar debalancedebalance-- File : hello2-1.lua -- Purpose : Demonstration of Luerl interface. -- See : ./examples/hello/hello2.erl print("(6) Hello, File 'hello2-1'!")rvirding-luerl-772b25e/examples/hello/hello2.erl0000664000232200023220000001455115114130670022204 0ustar debalancedebalance%% File : hello2.erl %% File : luerl/examples/hello/hello2.erl %% Purpose : Demonstration of the Luerl interface. %% Author : Henning Diedrich %% Use : $ cd examples/hello && erlc hello2.erl && erl -pa ../../ebin -s hello2 run -s init stop -noshell %% Or : $ make examples -module(hello2). -export([run/0]). run() -> io:format("-------------------------------------------~n"), io:format("This is an assortment of samples and tests.~n"), io:format("-------------------------------------------~n"), io:format("It's a comprehensive demo of the interface.~n"), io:format("Please check out the source to learn more.~n"), St0A = luerl:init(), % execute a string luerl:do("print(\"(1) Hello, Robert!\")", St0A), luerl:do(<<"print(\"(2) Hello, Roberto!\")">>, St0A), % execute a string, get a result {ok,A,_} = luerl:do("return 1 + 1", St0A), {ok,A,_} = luerl:do(<<"return 1 + 1">>, St0A), io:format("(5) 1 + 1 = ~p!~n", [A]), % execute a file luerl:dofile("./hello2-1.lua", St0A), % execute a file, get a result {ok,B,_} = luerl:dofile("./hello2-2.lua", St0A), io:format("(7) 2137 * 42 = ~p?~n", [B]), % execute a standard function luerl:call_function_dec([print], [<<"(8) Hello, standard print function!">>], St0A), luerl:call_function_dec([print], [<<"(9) Hello, standard print function!">>], St0A), {ok,Result1,_} = luerl:call_function_dec([table,pack], [<<"a">>,<<"b">>,42], St0A), {ok,Result1,_} = luerl:call_function_dec([table,pack], [<<"a">>,<<"b">>,42], St0A), io:format("(10) ~p?~n", [Result1]), % separately parse, then execute (doubles (11) and Chunk1 as assertion) St1A = luerl:init(), {ok,Chunk1,St1B} = luerl:load("print(\"(11) Hello, Chunk 1!\")", St1A), {ok,Chunk1,_} = luerl:load(<<"print(\"(11) Hello, Chunk 1!\")">>, St1A), luerl:call_chunk(Chunk1, [], St1B), % separately parse, then execute (doubles (12) and Chunk2 as assertion) St2A = luerl:init(), {ok,Chunk2,St2B} = luerl:load("function chunk2() print(\"(12) Hello, Chunk 2!\") end", St2A), {ok,Chunk2,_} = luerl:load(<<"function chunk2() print(\"(12) Hello, Chunk 2!\") end">>, St2A), {ok,Result2,St2C} = luerl:call_chunk(Chunk2, [], St2B), {ok,Result2,St2D} = luerl:do(<<"function chunk2() print(\"(12) Hello, Chunk 2!\") end">>, St2A), luerl:call_function_dec([chunk2], [], St2C), luerl:call_function_dec([chunk2], [], St2D), % separately parse, then execute a file. The file defines a function no() St3A = luerl:init(), {ok,Chunk3,St3B} = luerl:loadfile("./hello2-3.lua", St3A), {ok,_Result3,St3C} = luerl:call_chunk(Chunk3, St3B), {ok,[],_} = luerl:call_function_dec([no], [], St3C), % separately parse, then execute, get a result St4A = luerl:init(), {ok,Chunk4,St4B} = luerl:load("return '(17b) Marvelous wheater today, isn°t it!'", St4A), {ok,Chunk4,_} = luerl:load(<<"return '(17b) Marvelous wheater today, isn°t it!'">>, St4A), {ok,Result4,_} = luerl:call_chunk(Chunk4, [], St4B), io:format("(17) And I say: ~p~n", [Result4]), % separately parse, then execute a file, get a result St5A = luerl:init(), {ok,Chunk5,St5B} = luerl:loadfile("./hello2-4.lua", St5A), {ok,Result5,_} = luerl:call_chunk(Chunk5, St5B), io:format("(18) And he says: ~p~n", [Result5]), % Same as above, passing State in all times. % create state New = luerl:init(), {ok,_,_New2} = luerl:do("print '(19) hello generix'", New), % change state {ok,_,State0} = luerl:do("a = 1000", New), {ok,_,State01} = luerl:do("a = 1000", New), % execute a string, using passed in State0 luerl:do("print('(22) ' .. a+2)", State0), luerl:do(<<"print('(23) ' .. a+3)">>, State0), % execute a string, get a result from passed in State0 {ok,E,_} = luerl:do("return 4 * a", State0), {ok,E,_} = luerl:do(<<"return 4 * a">>, State0), io:format("(24) 4 x a = ~p!~n", [E]), % execute a string, get a result, change State0 {ok,Z,State02} = luerl:do("a = 123; return a * 3", State01), {ok,Z,State03} = luerl:do(<<"return (3 * a)">>, State02), io:format("(25) a = ~p~n", [Z]), % execute a file using passed in state luerl:dofile("./hello2-5.lua", State03), % execute a file that changes the State0 {ok,_,State04} = luerl:dofile("./hello2-6.lua", State03), luerl:do("print('(27) (b) ' .. a)", State04), % execute a file, get a result {ok,F,State05} = luerl:dofile("./hello2-7.lua", State04), io:format("(28) F: ~ts~n", [F]), % execute a file that changes the State0, and get a value back {ok,F,State06} = luerl:dofile("./hello2-7.lua", State05), io:format("(29) F: ~ts = ", [F]), luerl:do("print('(30) F: ' .. a)", State06), % separately parse, then execute {ok,Chunk11,_} = luerl:load("print(\"(31) Hello, \" .. a .. \"!\")", State06), {ok,Chunk11,State07} = luerl:load(<<"print(\"(31) Hello, \" .. a .. \"!\")">>, State06), luerl:call_chunk(Chunk11,State07), % separately parse, then execute a file. The file defines a function old() {ok,Chunk12,St7} = luerl:loadfile("./hello2-8.lua", State07), {ok,_Result12,State07A} = luerl:call_chunk(Chunk12,St7), luerl:call_function_dec([old],[],State07A), % separately parse, then execute, get a result {ok,Chunk13,St8} = luerl:load("a = '(30a)' .. a .. ' (this is Greek)'; return a", State07), {ok,Chunk13,_} = luerl:load(<<"a = '(30a)' .. a .. ' (this is Greek)'; return a">>, State07), {ok,Result07,State08} = luerl:call_chunk(Chunk13, St8), io:format("(34) And again I said: ~ts~n", [Result07]), % separately parse, then execute a file, get a result. The file defines confirm(p) {ok,Chunk14,St9} = luerl:loadfile("./hello2-9.lua", State08), {ok,Result14,State14} = luerl:call_chunk(Chunk14, St9), io:format("(35) And twice: ~ts~n", [Result14]), {ok,Result14A,_} = luerl:call_function_dec([confirm], [<<"Is it?">>], State14), io:format("(36) Well: ~ts~n", [Result14A]), % execute a file, get the decoded result of a table {ok,Result15,_} = luerl:dofile("./hello2-10.lua", State14), io:format("(37) Decoded table: ~p~n", [Result15]), io:format("done~n"). rvirding-luerl-772b25e/examples/euler/0000775000232200023220000000000015114130670020316 5ustar debalancedebalancervirding-luerl-772b25e/examples/euler/problem_001.lua0000664000232200023220000000051215114130670023037 0ustar debalancedebalance-- If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. -- Find the sum of all the multiples of 3 or 5 below 1000. LIMIT = 1000 sum = 0 for ii = 1, (LIMIT-1) do if (ii%3 == 0) or (ii%5 == 0) then sum = sum + ii end end print(sum) return sum rvirding-luerl-772b25e/examples/euler/problem_010.lua0000664000232200023220000000160415114130670023042 0ustar debalancedebalance-- The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. -- Find the sum of all the primes below two million. -- LIMIT = 2000000 LIMIT = 2000 function primes_below(limit) found_primes = { } local function divisible_by_known_primes(num) for _, v in ipairs(found_primes) do if num % v == 0 then return true end end return false end local function next_prime(found_primes, last_prime) if last_prime == nil then table.insert(found_primes, 2) return 2 end val = found_primes[#found_primes] repeat val = (val == 2) and 3 or val + 2 until(not divisible_by_known_primes(val)) table.insert(found_primes, val) if val > limit then return nil else return val end end return next_prime, found_primes, nil end sum = 0 for ii in primes_below(LIMIT) do sum = sum + ii end print(sum) return sum rvirding-luerl-772b25e/examples/euler/problem_006.lua0000664000232200023220000000116715114130670023053 0ustar debalancedebalance-- The sum of the squares of the first ten natural numbers is, -- 12 + 22 + ... + 102 = 385 -- The square of the sum of the first ten natural numbers is, -- (1 + 2 + ... + 10)2 = 552 = 3025 -- Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025 385 = 2640. -- Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. LIMIT = 100 sum, sum_squares = 0, 0 for ii = 1, LIMIT do sum, sum_squares = sum + ii, sum_squares + (ii * ii) end answer = (sum*sum) - sum_squares print(answer) return answer rvirding-luerl-772b25e/examples/euler/problem_008.lua0000664000232200023220000000263715114130670023060 0ustar debalancedebalance-- Find the greatest product of five consecutive digits in the 1000-digit number. -- NUMBER = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" NUMBER = "11111222221" largest = 0 for ii = 1, (#NUMBER-5) do digits = string.sub(NUMBER, ii, ii+5) sum = string.sub(digits, 1, 1) * string.sub(digits, 2, 2) * string.sub(digits, 3, 3) * string.sub(digits, 4, 4) * string.sub(digits, 5, 5) largest = (sum > largest) and sum or largest end print(largest) return largest rvirding-luerl-772b25e/examples/euler/problem_005.lua0000664000232200023220000000210715114130670023045 0ustar debalancedebalance-- 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. -- What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? LIMIT = 20 function prime_factors(n) local function factor(_, val) if n > 1 then while n % val > 0 do val = val + ( val == 2 and 1 or 2) if val * val > n then val = n end end n = n / val return val end end return factor, nil, 2 end function factorize(number) factors = {} for p in prime_factors(number) do factors[p] = factors[p] and factors[p] + 1 or 1 end return factors end function collapse(dict1, dict2) dict = {} for key, val in pairs(dict1) do dict[key] = math.max(val, dict2[key] or 0) end for key, val in pairs(dict2) do dict[key] = math.max(val, dict1[key] or 0) end return dict end factors = {} for ii = 2, LIMIT do factors = collapse(factors, factorize(ii)) end product = 1 for key, val in pairs(factors) do product = product * (key ^ val) end print(product) return product rvirding-luerl-772b25e/examples/euler/problem_003.lua0000664000232200023220000000111115114130670023035 0ustar debalancedebalance-- The prime factors of 13195 are 5, 7, 13 and 29. -- What is the largest prime factor of the number 600851475143 ? -- NUMBER_TO_FACTOR = 600851475143 NUMBER_TO_FACTOR = 13195 limit = math.sqrt(NUMBER_TO_FACTOR) primes = { 3 } function divisible_by_any(val, array) for ii, num in ipairs(array) do if (val % num) == 0 then return true end end return false end for ii = 5, limit, 2 do if not divisible_by_any(ii, primes) then table.insert(primes, ii) if NUMBER_TO_FACTOR % ii == 0 then factor = ii end end end print(factor) return factor rvirding-luerl-772b25e/examples/euler/euler.erl0000664000232200023220000000221115114130670022132 0ustar debalancedebalance%% File : euler.erl %% Purpose : Running complex lua with luerl %% Use: erlc -I ../../src euler.erl && erl -pa ../../ebin -s euler run -s init stop -noshell %% Or: make -module(euler). -export([run/0, run/2]). run() -> run("./problem_001.lua", 233168), run("./problem_002.lua", 4613732), run("./problem_003.lua", 29), run("./problem_004.lua", 36863), run("./problem_005.lua", 232792560), run("./problem_006.lua", 25164150), run("./problem_007.lua", 617), run("./problem_008.lua", 32), run("./problem_009.lua", 31875000), run("./problem_010.lua", 277050.0), ok. run(File, Solution) -> Lua0 = luerl:init(), {ok, Form, Lua1} = luerl:loadfile(File, Lua0), case timer:tc(luerl, call_chunk, [Form, [], Lua1]) of {T, {ok, [Return], _}} when Return == Solution -> io:format("~s (returned ~p in ~pus)~n", [File, Return, T]); {T, {ok, [Return], _}} -> io:format("~s (expected ~p but got ~p in ~pus)~n", [File, Solution, Return, T]); {_, {lua_error, Error, State}} -> io:format("luerl error: ~p~n", [{Error,State}]); {_, {error, Error, State}} -> io:format("Error: ~p~n", [{Error,State}]) end. rvirding-luerl-772b25e/examples/euler/problem_009.lua0000664000232200023220000000063515114130670023055 0ustar debalancedebalance-- A Pythagorean triplet is a set of three natural numbers, a b c, for which, -- a2 + b2 = c2 -- For example, 32 + 42 = 9 + 16 = 25 = 52. -- There exists exactly one Pythagorean triplet for which a + b + c = 1000. -- Find the product abc. for a = 2, 499 do for b = 2, 499 do c = (1000 - a) - b if a^2 + b^2 == c^2 then answer = a*b*c print(answer) return answer end end end rvirding-luerl-772b25e/examples/euler/problem_002.lua0000664000232200023220000000073515114130670023047 0ustar debalancedebalance-- Each new term in the Fibonacci sequence is generated by adding the previous two terms. -- By starting with 1 and 2, the first 10 terms will be: -- 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... -- By considering the terms in the Fibonacci sequence whose values do not exceed four million, -- find the sum of the even-valued terms. LIMIT = 4000000 a, b, sum = 1, 2, 0 while b <= LIMIT do if b % 2 == 0 then sum = sum + b end a, b = b, a + b end print(sum) return sum rvirding-luerl-772b25e/examples/euler/Makefile0000664000232200023220000000044015114130670021754 0ustar debalancedebalanceEXAMPLES = euler ROOTDIR = ../.. SRCDIR = $(ROOTDIR)/src BEAMDIR = $(ROOTDIR)/ebin all: $(EXAMPLES) clean: rm -f *.beam erl_crash.dump .SECONDARY: %.beam: %.erl $(SRCDIR)/*.hrl erlc -I $(SRCDIR) $< %: %.beam erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell .PHONY: all clean rvirding-luerl-772b25e/examples/euler/problem_007.lua0000664000232200023220000000147015114130670023051 0ustar debalancedebalance-- By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. -- What is the 10 001st prime number? -- LIMIT = 10001 LIMIT = 113 function primes(count) found_primes = { 2 } local function divisible_by_known_primes(num) for _, v in ipairs(found_primes) do if num % v == 0 then return true end end return false end local function next_prime(found_primes, _) val = found_primes[#found_primes] repeat val = (val == 2) and 3 or val + 2 until(not divisible_by_known_primes(val)) table.insert(found_primes, val) if #found_primes > count then return nil else return val end end return next_prime, found_primes, 3 end for ii in primes(LIMIT) do highest = ii end print(highest) return highest rvirding-luerl-772b25e/examples/euler/problem_004.lua0000664000232200023220000000121215114130670023040 0ustar debalancedebalance-- A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 99. -- Find the largest palindrome made from the product of two 3-digit numbers. function is_palindrome(number) local str = number .. '' for ii = 1, (#str / 2) do if string.byte(str,ii) ~= string.byte(str, -ii) then return false end end return true end LOW = 100 --HIGH = 999 HIGH = 199 highest = 0 for ii = LOW, HIGH do for jj = LOW, HIGH do num = ii * jj if is_palindrome(num) then highest = (num > highest) and num or highest end end end print(highest) return highest rvirding-luerl-772b25e/src/0000775000232200023220000000000015114130670016153 5ustar debalancedebalancervirding-luerl-772b25e/src/NOTES0000664000232200023220000001335515114130670016775 0ustar debalancedebalance Copyright (c) 2013 Robert Virding 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. Implementation Notes -------------------- Syntax ------ We are almost able to represent the Lua syntax as an LALR(1) grammar. The only place this doesn't work is when a statement can be a function call as it clashes when it can also be a varlist. We get around this by using the more general prefixexp and doing a post-test to check that it is a functioncall. This works together with the varlist. Data ---- For the Lua data types we internally use the corresponding Erlang: nil - nil true/false - true/false strings - binaries numbers - floats tables - #table{} with array for keys 1..n, ordict for rest userdata - #userdata{} function - #function{} or {function,Fun} thread - #thread{} See luerl.hrl for the field names of the records. All tables are combinations of ttdicts and arrays. In each table an array is used for integer keys >= 1 while an ttdict is used for all other keys. We use this information when building/processing tables. Seems like Lua internally stores elements as a (unordered) sequence of key-value elements, except maybe for the table part. Some tests show that using ttdicts for all elements results the system being 10-20% slower and using more memory. So using the array module for positive integer keys seems a reasonable choice. Direct read/write access is fast, but "shifting" access for example in table.insert/remove is not that efficient. Most table functions work after a fashion even in the "undefined" case all the elements don't have keys only in 1..n, but it seems like the order in which elements were added affects the outcome. We don't have any such information available. We do try to do something reasonable that sort of mirrors the Lua functions. Should we or should we be strict? The table table can be either an ordict, an array, use the process dictionary, or an ETS table; these are accessed through macros. To use ETS would need a bigger change as copying the whole table for each access would be very inefficient. Either use bags and have one per table or use sets and have the ETS key as {Tab,Key}. Machine ------- The VM is a hybrid. It uses normal Erlang function calls for Luerl calls and blocks and has a small instruction set for operations inside a block. This should make it not too difficult to compile down to straight Erlang in the future. Blocks keep variables in tuples. There are two variable types depending on how they are defined: - Local variables that are used in this block and sub-blocks, but not used in any functions defined in the blocks. These are kept in a stack of tuples, the LocalVars or Lvs, and referenced by offset in stack and offset in tuple. - Environment variables that are defined in functions which are defined in this block or in sub-blocks. This mean they must be kept around as long as the functions are alive and are stored in the global heap as each invocation can modify them. They are kept in a stack of references, the EnvironmentVars or Evs, to tuples in the global heap and referenced by offset in stack and offset in tuple. A function contains a reference to the stack of environment variables which existed when it was created. Note that the mutable nature of Lua data means that these can be modified and the changes must be visible to every function which references them. There is also a stack containing arguments and temporary values. This is stack is "global" in the sense that it is passed through all calls and blocks. It is also passed as an argument into functions implemented in Erlang. This is so that event of a Lua/Luerl GC the collector uses the stack to determine which data in the global heap is to be saved. The VM is a pure stack machine. To handle multiple return values we always return a list of values. The only place this is not done is in luerl_eval.erl when getting values from the environment where we can only have one value. This means a lot of calls to first_value/1 in luerl_emul.erl, but the consistency is worth it. Similarly all the arguments in a function call are passed in a list. The function then unpacks the list into its arguments, including '...'. All of the predefined libraries have an install/1 function. This is called when initialising Luerl; it does any library specific initialisation necessary and returns a table containing the functions in the library. We create a unique tag which is saved in the environment. This is used so we can implement 'break' with a simple throw. The thrown value includes the tag so we can uniquely catch it and not get confused with a throw/error/exit from the erlang code. Compiler -------- The compiler has state at different levels: - In luerl_comp there is #comp{} containing code, options and errors. - In the #cst{} between the compiler modules for data outside the code. This empty so far. - Inside and local to the compiler modules. All the compiler modules are written so that they chain a status argument through their code, even if it not used. When they are not used we just send the atom 'nil' through and check it comes out "the other end". Lua implementation "features" ----------------------------- When "integers" are wanted then float input values are often "rounded" to the correct float value. So 1.3 --> 1.0 and 3.7 --> 4.0. rvirding-luerl-772b25e/src/luerl_util.erl0000664000232200023220000001105615114130670021042 0ustar debalancedebalance%% Copyright (c) 2019 Robert Virding %% %% 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. %% File : luerl_util.erl %% Purpose : Utility functions for Luer. -module(luerl_util). -include("luerl.hrl"). ?MODULEDOC(false). -export([errname_info/1]). %% Convert error names to errnos and strings. errname_info(Name) -> #{errno => get_errno(Name), errstr => erl_posix_msg:message(Name)}. %% Made using the following command (`errno' from the moreutils package): %% `errno -l | sort -k2 -n | awk '{print "get_errno("tolower($1)") -> "$2";"}'' get_errno(eperm) -> 1; get_errno(enoent) -> 2; get_errno(esrch) -> 3; get_errno(eintr) -> 4; get_errno(eio) -> 5; get_errno(enxio) -> 6; get_errno(e2big) -> 7; get_errno(enoexec) -> 8; get_errno(ebadf) -> 9; get_errno(echild) -> 10; get_errno(eagain) -> 11; get_errno(ewouldblock) -> 11; get_errno(enomem) -> 12; get_errno(eacces) -> 13; get_errno(efault) -> 14; get_errno(enotblk) -> 15; get_errno(ebusy) -> 16; get_errno(eexist) -> 17; get_errno(exdev) -> 18; get_errno(enodev) -> 19; get_errno(enotdir) -> 20; get_errno(eisdir) -> 21; get_errno(einval) -> 22; get_errno(enfile) -> 23; get_errno(emfile) -> 24; get_errno(enotty) -> 25; get_errno(etxtbsy) -> 26; get_errno(efbig) -> 27; get_errno(enospc) -> 28; get_errno(espipe) -> 29; get_errno(erofs) -> 30; get_errno(emlink) -> 31; get_errno(epipe) -> 32; get_errno(edom) -> 33; get_errno(erange) -> 34; get_errno(edeadlk) -> 35; get_errno(edeadlock) -> 35; get_errno(enametoolong) -> 36; get_errno(enolck) -> 37; get_errno(enosys) -> 38; get_errno(enotempty) -> 39; get_errno(eloop) -> 40; get_errno(enomsg) -> 42; get_errno(eidrm) -> 43; get_errno(echrng) -> 44; get_errno(el2nsync) -> 45; get_errno(el3hlt) -> 46; get_errno(el3rst) -> 47; get_errno(elnrng) -> 48; get_errno(eunatch) -> 49; get_errno(enocsi) -> 50; get_errno(el2hlt) -> 51; get_errno(ebade) -> 52; get_errno(ebadr) -> 53; get_errno(exfull) -> 54; get_errno(enoano) -> 55; get_errno(ebadrqc) -> 56; get_errno(ebadslt) -> 57; get_errno(ebfont) -> 59; get_errno(enostr) -> 60; get_errno(enodata) -> 61; get_errno(etime) -> 62; get_errno(enosr) -> 63; get_errno(enonet) -> 64; get_errno(enopkg) -> 65; get_errno(eremote) -> 66; get_errno(enolink) -> 67; get_errno(eadv) -> 68; get_errno(esrmnt) -> 69; get_errno(ecomm) -> 70; get_errno(eproto) -> 71; get_errno(emultihop) -> 72; get_errno(edotdot) -> 73; get_errno(ebadmsg) -> 74; get_errno(eoverflow) -> 75; get_errno(enotuniq) -> 76; get_errno(ebadfd) -> 77; get_errno(eremchg) -> 78; get_errno(elibacc) -> 79; get_errno(elibbad) -> 80; get_errno(elibscn) -> 81; get_errno(elibmax) -> 82; get_errno(elibexec) -> 83; get_errno(eilseq) -> 84; get_errno(erestart) -> 85; get_errno(estrpipe) -> 86; get_errno(eusers) -> 87; get_errno(enotsock) -> 88; get_errno(edestaddrreq) -> 89; get_errno(emsgsize) -> 90; get_errno(eprototype) -> 91; get_errno(enoprotoopt) -> 92; get_errno(eprotonosupport) -> 93; get_errno(esocktnosupport) -> 94; get_errno(enotsup) -> 95; get_errno(eopnotsupp) -> 95; get_errno(epfnosupport) -> 96; get_errno(eafnosupport) -> 97; get_errno(eaddrinuse) -> 98; get_errno(eaddrnotavail) -> 99; get_errno(enetdown) -> 100; get_errno(enetunreach) -> 101; get_errno(enetreset) -> 102; get_errno(econnaborted) -> 103; get_errno(econnreset) -> 104; get_errno(enobufs) -> 105; get_errno(eisconn) -> 106; get_errno(enotconn) -> 107; get_errno(eshutdown) -> 108; get_errno(etoomanyrefs) -> 109; get_errno(etimedout) -> 110; get_errno(econnrefused) -> 111; get_errno(ehostdown) -> 112; get_errno(ehostunreach) -> 113; get_errno(ealready) -> 114; get_errno(einprogress) -> 115; get_errno(estale) -> 116; get_errno(euclean) -> 117; get_errno(enotnam) -> 118; get_errno(enavail) -> 119; get_errno(eisnam) -> 120; get_errno(eremoteio) -> 121; get_errno(edquot) -> 122; get_errno(enomedium) -> 123; get_errno(emediumtype) -> 124; get_errno(ecanceled) -> 125; get_errno(enokey) -> 126; get_errno(ekeyexpired) -> 127; get_errno(ekeyrevoked) -> 128; get_errno(ekeyrejected) -> 129; get_errno(eownerdead) -> 130; get_errno(enotrecoverable) -> 131; get_errno(erfkill) -> 132; get_errno(ehwpoison) -> 133; get_errno(_) -> 0. rvirding-luerl-772b25e/src/luerl_lib_bit32.erl0000664000232200023220000001427215114130670021641 0ustar debalancedebalance%% Copyright (c) 2014-2018 Łukasz Biedrycki %% %% 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. %% File : luerl_lib_bit32.erl %% Author : Łukasz Biedrycki %% Purpose : The bit32 library for Luerl. %% This library has been deprecated in 5.3 but we still keep it. -module(luerl_lib_bit32). -include("luerl.hrl"). ?MODULEDOC(false). -export([install/1,fband/3,fbnot/3,fbor/3,fbtest/3,fbxor/3,flshift/3,frshift/3, farshift/3,flrotate/3,frrotate/3,fextract/3,freplace/3]). -import(luerl_lib, [badarg_error/3]). %Shorten this -define(MOST_SIGNIFICANT, 16#80000000). -define(LEAST_SIGNIFICANT, 16#00000001). -define(DEFAULT_BAND, 4294967295). -define(DEFAULT_BOR, 0). -define(DEFAULT_BXOR, 0). install(St) -> luerl_heap:alloc_table(table(), St). table() -> [{<<"band">>,#erl_mfa{m=?MODULE,f=fband}}, {<<"bnot">>,#erl_mfa{m=?MODULE,f=fbnot}}, {<<"bor">>,#erl_mfa{m=?MODULE,f=fbor}}, {<<"btest">>,#erl_mfa{m=?MODULE,f=fbtest}}, {<<"bxor">>,#erl_mfa{m=?MODULE,f=fbxor}}, {<<"lshift">>,#erl_mfa{m=?MODULE,f=flshift}}, {<<"rshift">>,#erl_mfa{m=?MODULE,f=frshift}}, {<<"arshift">>,#erl_mfa{m=?MODULE,f=farshift}}, {<<"lrotate">>,#erl_mfa{m=?MODULE,f=flrotate}}, {<<"rrotate">>,#erl_mfa{m=?MODULE,f=frrotate}}, {<<"extract">>,#erl_mfa{m=?MODULE,f=fextract}}, {<<"replace">>,#erl_mfa{m=?MODULE,f=freplace}} ]. fband(_, As, St) -> case luerl_lib:args_to_integers(As) of L when is_list(L) -> {[aband(L)], St}; error -> badarg_error('band', As, St) end. aband([]) -> ?DEFAULT_BAND; aband([X|T]) -> aband(T, checkint32(X)). aband([], A) -> float(A); aband([X|T], A) -> aband(T, checkint32(X) band A). fbnot(_, As, St) -> case luerl_lib:args_to_integers(As) of [N|_] -> NotN = bnot checkint32(N), {[float(NotN)], St}; error -> badarg_error('bnot', As, St) end. fbor(_, As, St) -> case luerl_lib:args_to_integers(As) of L when is_list(L) -> {[abor(L)], St}; error -> badarg_error('bor', As, St) end. abor([]) -> ?DEFAULT_BOR; abor([X|T]) -> abor(T, checkint32(X)). abor([], A) -> float(A); abor([X|T], A) -> abor(T, checkint32(X) bor A). fbtest(_, As, St) -> case luerl_lib:args_to_integers(As) of L when is_list(L) -> {[aband(L) /= 0], St}; error -> badarg_error('btest', As, St) end. fbxor(_, As, St) -> case luerl_lib:args_to_integers(As) of L when is_list(L) -> {[abxor(L)], St}; error -> badarg_error('bxor', As, St) end. abxor([]) -> ?DEFAULT_BXOR; abxor([X|T]) -> abxor(T, checkint32(X)). abxor([], A) -> float(A); abxor([X|T], A) -> abxor(T, checkint32(X) bxor A). flshift(_, As, St) -> case luerl_lib:args_to_integers(As) of [X,Y|_] -> {[float(checkint32(X) bsl trunc(Y))], St}; _ -> badarg_error('lshift', As, St) end. frshift(_, As, St) -> case luerl_lib:args_to_integers(As) of [X,Y|_] -> {[float(checkint32(X) bsr trunc(Y))], St}; _ -> badarg_error('rshift', As, St) end. farshift(_, As, St) -> case luerl_lib:args_to_integers(As) of [X,Y|_] -> Disp = trunc(Y), case Disp > 0 of true -> {[float(checkint32(X) bsr trunc(Y))], St}; false -> {[float(checkint32(X) bsl abs(trunc(Y)))], St} end; _ -> badarg_error('arshift', As, St) end. flrotate(_, As, St) -> case luerl_lib:args_to_integers(As) of [X,Y|_] -> {[float(lrotate(checkint32(X), trunc(Y)))], St}; _ -> badarg_error('lrotate', As, St) end. frrotate(_, As, St) -> case luerl_lib:args_to_integers(As) of [X,Y|_] -> {[float(rrotate(checkint32(X), trunc(Y)))], St}; _ -> badarg_error('rrotate', As, St) end. fextract(_, As, St) -> case luerl_lib:args_to_integers(As) of [N,Field,Width|_] -> {[float(extract(N, Field, Width, As, St))], St}; [N,Field|_] -> {[float(extract(N, Field, 1, As, St))], St}; _ -> badarg_error('extract', As, St) end. freplace(_, As, St) -> case luerl_lib:args_to_integers(As) of [N,V,Field,Width|_] -> {[float(replace(N, V, Field, Width, As, St))], St}; [N,V,Field|_] -> {[float(replace(N, V, Field, 1, As, St))], St}; _ -> badarg_error('replace', As, St) end. %% Internal lrotate(X, Y) when Y < 0 -> rrotate(X, abs(Y)); lrotate(X, Y) when Y == 0 -> X; lrotate(X1, Y) -> Most = X1 band ?MOST_SIGNIFICANT, X2 = uint32(X1 bsl 1), X3 = X2 bor (Most bsr 31), lrotate(X3, Y - 1). rrotate(X, Y) when Y < 0 -> lrotate(X, abs(Y)); rrotate(X, Y) when Y == 0 -> X; rrotate(X1, Y) -> Least = X1 band ?LEAST_SIGNIFICANT, X2 = X1 bsr 1, X3 = X2 bor (Least bsl 31), rrotate(X3, Y - 1). uint32(N) -> <> = <>, Res. checkint32(N) -> uint32(trunc(N)). ge0(N, Where, As, St) -> case N >= 0 of true -> N; false -> badarg_error(Where, As, St) end. gt0(N, Where, As, St) -> case N > 0 of true -> N; false -> badarg_error(Where, As, St) end. le(N, V, Where, As, St) -> case N =< V of true -> N; false -> badarg_error(Where, As, St) end. extract(N1, Field1, Width1, As, St) -> N2 = checkint32(N1), Field2 = trunc(Field1), Width2 = trunc(Width1), _ = ge0(Field2, 'extract', As, St), _ = gt0(Width2, 'extract', As, St), _ = le(Field2 + Width2, 32, 'extract', As, St), trunc(N2 / math:pow(2, Field2)) rem trunc(math:pow(2, Width2)). replace(N1, V1, Field1, Width1, As, St) -> N2 = checkint32(N1), V2 = checkint32(V1), Field2 = trunc(Field1), Width2 = trunc(Width1), _ = ge0(Field2, 'replace', As, St), _ = gt0(Width2, 'replace', As, St), _ = le(Field2 + Width2, 32, 'extract', As, St), Field3 = trunc(math:pow(2, Field2)), Width3 = trunc(math:pow(2, Width2)), FW = Field3 * Width3, (N2 rem Field3) + (V2 rem Width3) * Field3 + trunc(N2 div FW) * FW. rvirding-luerl-772b25e/src/luerl.hrl0000664000232200023220000002163315114130670020012 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl.hrl %% Author : Robert Virding %% Purpose : The basic macros/records for Luerl. %% We include the whole environment in one structure even if fields %% come from logically different parts. This make it easier to pass %% around but does mean that there will be more explicit fiddleling to %% get it right. See block/2 and functioncall/4 for examples of this. -record(luerl, {tabs, %Table table envs, %Environment table usds, %Userdata table fncs, %Function table g, %Global table %% stk=[], %Current stack cs=[], %Current call stack %% meta=[], %Data type metatables rand, %Random state tag, %Unique tag trace_func=none, %Trace function trace_data, %Trace data private=#{} }). %% Table structure. -record(tstruct, {data, %Data table/array free, %Index free list next %Next index }). %% Metatables for atomic datatypes. -record(meta, {nil=nil, boolean=nil, number=nil, string=nil }). %% Frames for the call stack. %% Call return frame -record(call_frame, {func,args, %Function, arguments lvs, %Local variables env, %Environment is=[], %Instructions cont=[] %Continuation }). %% Loop break frame -record(loop_frame, {lvs, %Local variables stk, %Stack env, %Environment is=[], %Instructions cont=[] %Continuation }). %% Current line -record(current_line, {line, %Line file %File name }). %% Return -record(return, {rets %Return values }). %% Data types. -record(tref, {i}). %Table reference, index -define(IS_TREF(T), is_record(T, tref)). -record(table, {a,d=[],meta=nil}). %Table type, array, dict, meta -record(eref, {i}). %Environment reference, index -define(IS_EREF(E), is_record(E, eref)). -record(usdref, {i}). %Userdata reference, index -define(IS_USDREF(U), is_record(U, usdref)). -record(userdata, {d,meta=nil}). %Userdata type, data and meta -record(thread, {}). %Thread type %% There are two function types, the Lua one, and the Erlang one. %% The environment with upvalues is defined when the function is %% referenced and can vary if the function is referenced many %% times. Hence it is in the reference not in the the definition. -record(funref, {i,env=[]}). %Function reference -define(IS_FUNREF(F), is_record(F, funref)). -record(lua_func,{anno=[], %Annotation funrefs=[], %Functions directly referenced lsz, %Local var size %% loc=not_used, %Local var block template esz, %Env var size %% env=not_used, %Local env block template pars, %Parameter types body}). %Code block -define(IS_LUAFUNC(F), is_record(F, lua_func)). -record(erl_func,{code}). %Erlang code (fun) -define(IS_ERLFUNC(F), is_record(F, erl_func)). -record(erl_mfa,{m,f,a}). %Erlang code (MFA) -define(IS_ERLMFA(F), is_record(F, erl_mfa)). %% Test if it a function, of either sort. -define(IS_FUNCTION(F), (?IS_FUNREF(F) orelse ?IS_ERLFUNC(F) orelse ?IS_ERLMFA(F))). %% Testing for integers/integer floats or booleans. -define(IS_FLOAT_INT(N), (round(N) == N)). -define(IS_FLOAT_INT(N,I), ((I=round(N)) == N)). -define(IS_TRUE(X), (((X) =/= nil) and ((X) =/= false))). %% %% Global type specifications. %% %% The basic Luerl state. -type luerlstate() :: #luerl{}. %% Luerl data. -type luerldata() :: nil | boolean() | binary() | number() | #tref{} | %Table reference #usdref{} | %Userdata reference #eref{} | %Environment reference #funref{} | %Lua function reference #erl_func{} | %Erlang function #erl_mfa{}. %Erlang Mod, Func, Arg. %% Different methods for storing tables in the global data #luerl{}. %% Access through macros to allow testing with different storage %% methods. This is inefficient with ETS tables where it would %% probably be better to use bags and access with match/select. %% Set which table store to use. We check if we have full maps before %% we use them just to protect ourselves. -ifdef(HAS_FULL_KEYS). -define(TS_USE_MAPS, true). -else. -define(TS_USE_ARRAY, true). -endif. %% -define(TS_USE_ARRAY, true). -ifdef(TS_USE_MAPS). -define(MAKE_TABLE(), maps:new()). -define(GET_TABLE(N, Ts), maps:get(N, Ts)). -define(SET_TABLE(N, T, Ts), maps:put(N, T, Ts)). -define(UPD_TABLE(N, Upd, Ts), maps:update_with(N, Upd, Ts)). -define(DEL_TABLE(N, Ts), maps:remove(N, Ts)). -define(CHK_TABLE(N, Ts), maps:is_key(N, Ts)). -define(FILTER_TABLES(Pred, Ts), maps:filter(Pred, Ts)). -define(FOLD_TABLES(Fun, Acc, Ts), maps:fold(Fun, Acc, Ts)). -endif. -ifdef(TS_USE_ARRAY). %% Use arrays to handle tables. We leave the default value as undefined. -define(MAKE_TABLE(), array:new()). -define(GET_TABLE(N, Ar), array:get(N, Ar)). -define(SET_TABLE(N, T, Ar), array:set(N, T, Ar)). -define(UPD_TABLE(N, Upd, Ar), array:set(N, (Upd)(array:get(N, Ar)), Ar)). -define(DEL_TABLE(N, Ar), array:reset(N, Ar)). -define(CHK_TABLE(N, Ar), ((N >= 0) andalso (array:get(N, Ar) =/= undefined))). -define(FILTER_TABLES(Pred, Ar), ((fun (___Def) -> ___Fil = fun (___K, ___V) -> case Pred(___K, ___V) of true -> ___V; false -> ___Def end end, array:sparse_map(___Fil, Ar) end)(array:default(Ar)))). -define(FOLD_TABLES(Fun, Acc, Ar), array:sparse_foldl(Fun, Acc, Ar)). -endif. -ifdef(TS_USE_ORDDICT). %% Using orddict to handle tables. -define(MAKE_TABLE(), orddict:new()). -define(GET_TABLE(N, Ts), orddict:fetch(N, Ts)). -define(SET_TABLE(N, T, Ts), orddict:store(N, T, Ts)). -define(UPD_TABLE(N, Upd, Ts), orddict:update(N, Upd, Ts)). -define(DEL_TABLE(N, Ts), orddict:erase(N, Ts)). -define(CHK_TABLE(N, Ts), orddict:is_key(N, Ts)). -define(FILTER_TABLES(Pred, Ts), orddict:filter(Pred, Ts)). -define(FOLD_TABLES(Fun, Acc, Ts), orddict:fold(Fun, Acc, Ts)). -endif. -ifdef(TS_USE_PD). %% Use the process dictionary to handle tables. -define(MAKE_TABLE(), ok). -define(GET_TABLE(N, Pd), get(N)). -define(SET_TABLE(N, T, Pd), put(N, T)). -define(UPD_TABLE(N, Upd, Pd), put(N, (Upd)(get(N)))). -define(DEL_TABLE(N, Pd), erase(N)). -define(CHK_TABLE(N, Pd), (get(N) =/= undefined)). -define(FILTER_TABLES(Pred, Pd), Pd). %This needs work -define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work -endif. -ifdef(TS_USE_ETS). %% Use ETS to handle tables. Must get return values right! -define(MAKE_TABLE(),ets:new(luerl_tables, [set])). -define(GET_TABLE(N, E), ets:lookup_element(E, N, 2)). -define(SET_TABLE(N, T, E), begin ets:insert(E, {N,T}), E end). -define(UPD_TABLE(N, Upd, E), begin ets:update_element(E, N, {2,(Upd)(ets:lookup_element(E, N, 2))}), E end). -define(DEL_TABLE(N, E), begin ets:delete(E, N), E end). -define(FILTER_TABLES(Pred, E), E). %This needs work -define(FOLD_TABLES(Fun, Acc, E), ets:foldl(fun ({___K, ___T}, ___Acc) -> Fun(___K, ___T, ___Acc) end, Acc, E)). -endif. %% Define CATCH to handle deprecated get_stacktrace/0 -ifdef(NEW_STACKTRACE). -define(CATCH(C, E, S), C:E:S ->). -else. -define(CATCH(C, E, S), C:E -> S = erlang:get_stacktrace(),). -endif. %% Define MODULEDOC and DOC to handle module and function documentation. -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. rvirding-luerl-772b25e/src/luerl_lib_package.erl0000664000232200023220000001577015114130670022315 0ustar debalancedebalance%% Copyright (c) 2013-2020 Robert Virding %% %% 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. %% File : luerl_lib_package.erl %% Author : Robert Virding %% Purpose : The package library for Luerl. %% These functions sometimes behave strangely in the Lua 5.2 %% libraries, but we try to follow them. Most of these functions KNOW %% that a table is a ttdict! We know that the erlang array has default %% value 'nil'. -module(luerl_lib_package). -include_lib("kernel/include/file.hrl"). -include("luerl.hrl"). ?MODULEDOC(false). %% The basic entry point to set up the function table. -export([install/1,require/3,searchpath/3,preload_searcher/3,lua_searcher/3]). %% Export some functions which can be called from elsewhere. -export([search_path/5]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this install(St0) -> St1 = luerl_emul:set_global_key(<<"require">>, #erl_mfa{m=?MODULE,f=require}, St0), {S,St2} = luerl_heap:alloc_table(searchers_table(), St1), {L,St3} = luerl_heap:alloc_table(loaded_table(), St2), {P,St4} = luerl_heap:alloc_table(preload_table(), St3), {T,St5} = luerl_heap:alloc_table(table(S, L, P), St4), {T,St5}. %% table() -> [{FuncName,Function}]. %% meta_table() -> [{TableName,Function}]. %% searchers_table() %% preloaded_table() %% loaded_table() table(S, L, P) -> [{<<"config">>,config()}, {<<"loaded">>,L}, {<<"preload">>,P}, {<<"path">>,path()}, {<<"searchers">>,S}, {<<"searchpath">>,#erl_mfa{m=?MODULE,f=searchpath}} ]. searchers_table() -> [{1.0,#erl_mfa{m=?MODULE,f=preload_searcher}}, {2.0,#erl_mfa{m=?MODULE,f=lua_searcher}}]. preload_table() -> []. loaded_table() -> []. %% meta_table() -> %% [{<<"__index">>,#erl_func{code=fun meta_values/2}} %% ]. %% config() %% path() %% meta_values() %% Generate initial data for tables. config() -> <<"/\n",";\n","?\n","!\n","-\n">>. %The defaults path() -> case os:getenv("LUA_PATH") of false -> <<"./?.lua;./?/init.lua">>; %Default path Path -> list_to_binary(Path) end. %% meta_values([_,<<"bert">>], St) -> %% {[<<"/\n",";\n","?\n","!\n","-\n">>],St}; %% meta_values(_, St) -> {[nil],St}. %Default undefined key %% searchpath(Name, Path [, Sep [, Rep]]) -> [File] | [nil|Files]. searchpath(_, As, St) -> case luerl_lib:conv_list(search_args(As), [lua_string,lua_string,lua_string,lua_string]) of [N,P,S,R] -> %Name, path, sep, rep Ret = case search_path(N, P, S, R, []) of {ok,File} -> [File]; {error,Tried} -> [nil,Tried] end, {Ret,St}; _ -> badarg_error(searchpath, As, St) end. search_args([N,P]) -> [N,P,<<".">>,<<"/">>]; search_args([N,P,S]) -> [N,P,S,<<"/">>]; search_args(As) -> As. %% search_path(Name, Path, Sep, Rep, Tried) -> {ok,File} | {error,Tried}. %% Search for a file in a path. Callable from Erlang. search_path(N0, P, S, R, Tried) -> N1 = binary:replace(N0, S, R, [global]), Ts = binary:split(P, <<";">>, [global]), search_path_loop(N1, Ts, Tried). search_path_loop(Name, [T|Ts], Tried) -> File = binary:replace(T, <<"?">>, Name, [global]), %% Test if file can be opened for reading. case file:read_file_info(File) of {ok,#file_info{access=A}} when A =:= read; A =:= read_write -> {ok,File}; _ -> search_path_loop(Name, Ts, Tried ++ [$',File,$',$\s]) end; search_path_loop(_, [], Tried) -> %Couldn't find it {error,unicode:characters_to_binary(Tried)}. -spec require(_, [_], _) -> {_,_} | no_return(). %To keep dialyzer quiet %% require([File|_], State) ->{Value,State}. %% Main require interface. require(_, As, St) -> case luerl_lib:conv_list(As, [lua_string]) of [Mod] -> do_require(Mod, St); error -> badarg_error(require, As, St) end. do_require(Mod, St0) -> {Pt,St1} = luerl_emul:get_global_key(<<"package">>, St0), case luerl_emul:get_table_keys(Pt, [<<"loaded">>,Mod], St1) of {nil,St2} -> %Not loaded {Ss,St3} = luerl_emul:get_table_key(Pt, <<"searchers">>, St2), {[Ldr|Extra],St4} = search_loaders(Mod, Ss, St3), {Val,St5} = luerl_emul:functioncall(Ldr, [Mod|Extra], St4), require_ret(Mod, Val, Pt, St5); {Val,St2} -> {[Val],St2} %Already loaded end. require_ret(Mod, Val, Pt, St0) -> Res = case luerl_lib:first_value(Val) of nil -> true; %Assign true to loaded entry __tmp -> __tmp end, St1 = luerl_emul:set_table_keys(Pt, [<<"loaded">>,Mod], Res, St0), {[Res],St1}. search_loaders(Mod, Tref, St) -> #table{a=Arr} = luerl_heap:get_table(Tref, St), Ls = array:sparse_to_list(Arr), search_loaders_loop(Mod, Ls, <<>>, St). search_loaders_loop(Mod, [nil|Ls], Estr, St) -> %Could find some of these search_loaders_loop(Mod, Ls, Estr, St); search_loaders_loop(Mod, [L|Ls], Estr, St0) -> %Try the next loader %% Call the searcher function case luerl_emul:functioncall(L, [Mod], St0) of %% Searcher found a loader. {[F|_],_}=Ret when ?IS_FUNCTION(F) -> Ret; %% Searcher found no loader. {[S|_],St1} when is_binary(S) -> Estr1 = <>, %Append new info string search_loaders_loop(Mod, Ls, Estr1, St1); {_,St1} -> %Should be nil or [] search_loaders_loop(Mod, Ls, Estr, St1) end; search_loaders_loop(Mod, [], Estr, St) -> %No successful loader found lua_error({no_module,Mod,Estr}, St). %% preload_searcher() %% lua_searcher() %% Predefined search functions in package.searchers. These must be Lua %% callable functions as they are visible. preload_searcher(_, As, St0) -> case luerl_lib:conv_list(As, [lua_string]) of [Mod] -> {Pre,St1} = luerl_emul:get_table_keys([<<"package">>,<<"preload">>], St0), case luerl_emul:get_table_key(Pre, Mod, St1) of {nil,St2} -> {[],St2}; {Val,St2} -> {[Val],St2} %Return the chunk end; error -> badarg_error(preload_searcher, As, St0) end. lua_searcher(_, As, St0) -> case luerl_lib:conv_list(As, [lua_string]) of [Mod] -> {Path,St1} = luerl_emul:get_table_keys([<<"package">>,<<"path">>], St0), case search_path(Mod, Path, <<".">>, <<"/">>, []) of {ok,File} -> Ret = luerl_comp:file(binary_to_list(File)), lua_searcher_ret(Ret, File, St1); {error,Tried} -> {[Tried],St1} end; error -> badarg_error(lua_searcher, As, St0) end. lua_searcher_ret({ok,Chunk}, File, St0) -> %% Wrap chunk in function to be consistent. {Func,St1} = luerl_emul:load_chunk(Chunk, St0), {[Func,File],St1}; lua_searcher_ret({error,[{_,Mod,E}|_],_}, _, St) -> Msg = unicode:characters_to_binary(Mod:format_error(E)), {[Msg],St}. rvirding-luerl-772b25e/src/luerl_lib_basic.erl0000664000232200023220000003364415114130670022003 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_lib_basic.erl %% Author : Robert Virding %% Purpose : The basic library for Luerl. -module(luerl_lib_basic). -include("luerl.hrl"). ?MODULEDOC(false). %% The basic entry point to set up the function table. -export([install/1,assert/3,error_call/3,collectgarbage/3,dofile/3, eprint/3,getmetatable/3,ipairs/3,ipairs_next/3, load/3,loadfile/3,loadstring/3, next/3,pairs/3,pcall/3,print/3, rawequal/3,rawget/3,rawlen/3,rawset/3, select/3,setmetatable/3,tonumber/3,tostring/3,type/3,unpack/3]). %% Export some functions which can be called from elsewhere. -export([print/2,type/1]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten these install(St) -> luerl_heap:alloc_table(table(), St). %% table() -> [{FuncName,Function}]. %% Caller will convert this list to the correct format. table() -> [{<<"_VERSION">>,<<"Lua 5.3">>}, %We are optimistic {<<"assert">>,#erl_mfa{m=?MODULE,f=assert}}, {<<"collectgarbage">>,#erl_mfa{m=?MODULE,f=collectgarbage}}, {<<"dofile">>,#erl_mfa{m=?MODULE,f=dofile}}, {<<"eprint">>,#erl_mfa{m=?MODULE,f=eprint}}, {<<"error">>,#erl_mfa{m=?MODULE,f=error_call}}, {<<"getmetatable">>,#erl_mfa{m=?MODULE,f=getmetatable}}, {<<"ipairs">>,#erl_mfa{m=?MODULE,f=ipairs}}, {<<"load">>,#erl_mfa{m=?MODULE,f=load}}, {<<"loadfile">>,#erl_mfa{m=?MODULE,f=loadfile}}, {<<"loadstring">>,#erl_mfa{m=?MODULE,f=loadstring}}, %For Lua 5.1 compatibility {<<"next">>,#erl_mfa{m=?MODULE,f=next}}, {<<"pairs">>,#erl_mfa{m=?MODULE,f=pairs}}, {<<"pcall">>,#erl_mfa{m=?MODULE,f=pcall}}, {<<"print">>,#erl_mfa{m=?MODULE,f=print}}, {<<"rawequal">>,#erl_mfa{m=?MODULE,f=rawequal}}, {<<"rawget">>,#erl_mfa{m=?MODULE,f=rawget}}, {<<"rawlen">>,#erl_mfa{m=?MODULE,f=rawlen}}, {<<"rawset">>,#erl_mfa{m=?MODULE,f=rawset}}, {<<"select">>,#erl_mfa{m=?MODULE,f=select}}, {<<"setmetatable">>,#erl_mfa{m=?MODULE,f=setmetatable}}, {<<"tonumber">>,#erl_mfa{m=?MODULE,f=tonumber}}, {<<"tostring">>,#erl_mfa{m=?MODULE,f=tostring}}, {<<"type">>,#erl_mfa{m=?MODULE,f=type}}, {<<"unpack">>,#erl_mfa{m=?MODULE,f=unpack}} %For Lua 5.1 compatibility ]. assert(_, As, St) -> case luerl_lib:boolean_value(As) of true -> {As,St}; false -> M = case As of [_,M0|_] -> M0; _ -> <<"assertion failed">> end, lua_error({assert_error,M}, St) end. collectgarbage(_, [], St) -> collectgarbage(nil, [<<"collect">>], St); collectgarbage(_, [<<"collect">>|_], St) -> {[],luerl_heap:gc(St)}; %% {[],St}; %No-op for the moment collectgarbage(_, _, St) -> %Ignore everything else {[],St}. eprint(_, Args, St) -> lists:foreach(fun (#tref{}=Tref) -> Tab = luerl_heap:get_table(Tref, St), io:format("~w ", [Tab]); (A) -> io:format("~w ", [A]) end, Args), io:nl(), {[],St}. -spec error_call(_, _, _) -> no_return(). %% error_call(Args, State) -> no_return(). %% Generate an error with an error string. error_call(_, [{tref, _}=T|_]=As, St0) -> case luerl_heap:get_metamethod(T, <<"__tostring">>, St0) of nil -> lua_error({error_call, As}, St0); Meta -> {Rets, St1} = luerl_emul:functioncall(Meta, [T], St0), lua_error({error_call, Rets}, St1) end; error_call(_, As, St) -> %Never returns! lua_error({error_call, As}, St). %% ipairs(Args, State) -> {[Func,Table,FirstKey],State}. %% Return a function which on successive calls returns successive %% key-value pairs of integer keys. We check that it is a table first %% when we access it. ipairs(_, [Tref|_], St) -> case luerl_heap:get_metamethod(Tref, <<"__ipairs">>, St) of nil -> {[#erl_mfa{m=?MODULE,f=ipairs_next},Tref,0],St}; Meta -> luerl_emul:functioncall(Meta, [Tref], St) end; ipairs(_, As, St) -> badarg_error(ipairs, As, St). ipairs_next(_, [A], St) -> ipairs_next(nil, [A,0], St); ipairs_next(_, [Tref,K|_], St) when ?IS_TREF(Tref), is_integer(K) -> %% Get the table. #table{a=Arr} = luerl_heap:get_table(Tref, St), Next = K + 1, case array:get(Next, Arr) of nil -> {[nil],St}; V -> {[Next,V],St} end; ipairs_next(_, As, St) -> badarg_error(ipairs, As, St). %% pairs(Args, State) -> {[Func,Table,Key],State}. %% Return a function to step over all the key-value pairs in a %% table. We check that it is a table first when we access it. pairs(_, [Tref|_], St) -> case luerl_heap:get_metamethod(Tref, <<"__pairs">>, St) of nil -> {[#erl_mfa{m=?MODULE,f=next},Tref,nil],St}; Meta -> luerl_emul:functioncall(Meta, [Tref], St) end; pairs(_, As, St) -> badarg_error(pairs, As, St). %% next(Args, State) -> {[Key,Value] | [nil], State}. %% Given a table and a key return the next key-value pair in the %% table, or nil if there is no next key. The key 'nil' gives the %% first key-value pair. next(_, [A], St) -> next(nil, [A,nil], St); next(_, [#tref{}=Tref,K|_], St) -> %% Get the table. #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St), if K == nil -> %% Find the first, start with the array. next_index(0, Arr, Dict, St); is_integer(K), K >= 1 -> next_index(K, Arr, Dict, St); is_float(K) -> case ?IS_FLOAT_INT(K, I) of true when I >= 1 -> next_index(I, Arr, Dict, St); _NegFalse -> %Not integer or negative next_key(K, Dict, St) end; true -> next_key(K, Dict, St) end; next(_, As, St) -> badarg_error(next, As, St). next_index(I0, Arr, Dict, St) -> case next_index_loop(I0+1, Arr, array:size(Arr)) of {I1,V} -> {[I1,V],St}; none -> %% Nothing in the array, take table instead. first_key(Dict, St) end. next_index_loop(I, Arr, S) when I < S -> case array:get(I, Arr) of nil -> next_index_loop(I+1, Arr, S); V -> {I,V} end; next_index_loop(_, _, _) -> none. first_key(Dict, St) -> case ttdict:first(Dict) of {ok,{K,V}} -> {[K,V],St}; error -> {[nil],St} end. next_key(K, Dict, St) -> case ttdict:next(K, Dict) of {ok,{N,V}} -> {[N,V],St}; error -> {[nil],St} end. %% print(Args, State) -> {[],State}. %% Receives any number of arguments and prints their values to %% stdout, using the tostring function to convert each argument to a %% string. print is not intended for formatted output, but only as a %% quick way to show a value, for instance for debugging. print(_, Args, St0) -> St1 = lists:foldl(fun (A, S0) -> {Str,S1} = luerl_lib:tostring(A, S0), print_arg(Str), S1 end, St0, Args), io:nl(), {[],St1}. print_arg(Str) -> Fun = fun (C, Acc) when C >= 128 -> [$?|Acc]; %Just mark with a ? %% Some special case control characters. (C, Acc) when C =:= $\t ; C =:= $\n ; C =:= $\v ; C =:= $\f ; C =:= $\r -> [C|Acc]; (C, Acc) when C =< 31 -> Acc; %Skip other control characters (C, Acc) -> [C|Acc] %Output the rest end, Chars = lists:foldr(Fun, [], binary_to_list(Str)), io:format("~s ", [Chars]). print(Args, St0) -> print(nil, Args, St0). %% rawequal([Arg,Arg|_], State) -> {[Bool],State}. %% rawlen([Object|_], State) -> {[Length],State}. %% rawget([Table,Key|_], State) -> {[Val],State)}. %% rawset([Table,Key,Value|_]], State) -> {[Table],State)}. rawequal(_, [A1,A2|_], St) -> {[A1 =:= A2],St}; rawequal(_, As, St) -> badarg_error(rawequal, As, St). rawlen(_, [A|_], St) when is_binary(A) -> {[float(byte_size(A))],St}; rawlen(_, [#tref{}=T|_], St) -> {[luerl_lib_table:raw_length(T, St)],St}; rawlen(_, As, St) -> badarg_error(rawlen, As, St). rawget(_, [#tref{}=Tref,Key|_], St) -> Val = luerl_heap:raw_get_table_key(Tref, Key, St), {[Val],St}; rawget(_, As, St) -> badarg_error(rawget, As, St). rawset(_, [Tref,nil=Key,_|_], St) -> lua_error({illegal_index,Tref,Key}, St); rawset(_, [#tref{}=Tref,Key,Val|_], St0) -> St1 = luerl_heap:raw_set_table_key(Tref, Key, Val, St0), {[Tref],St1}; rawset(_, As, St) -> badarg_error(rawset, As, St). %% select(Args, State) -> {[Element],State}. select(_, [<<$#>>|As], St) -> {[float(length(As))],St}; select(_, [A|As], St) -> Len = length(As), case luerl_lib:arg_to_integer(A) of N when is_integer(N), N > 0 -> {select_front(N, As, Len),St}; N when is_integer(N), N < 0 -> {select_back(-N, As, Len),St}; _ -> badarg_error(select, [A|As], St) end; select(_, As, St) -> badarg_error(select, As, St). select_front(N, As, Len) when N =< Len -> lists:nthtail(N-1, As); select_front(_, _, _) -> []. select_back(N, As, Len) when N =< Len -> lists:nthtail(Len-N, As); select_back(_, As, _) -> As. tonumber(_, [Arg], St) -> {[tonumber(luerl_lib:arg_to_number(Arg))],St}; tonumber(_, [Arg,B|_], St) -> {[tonumber(luerl_lib:arg_to_number(Arg, B))],St}; tonumber(_, As, St) -> badarg_error(tonumber, As, St). tonumber(Num) when is_number(Num) -> Num; tonumber(_) -> nil. %% tostring([Arg|_], Stated) -> {String,State}. %% Return the type as a string. tostring(_, [Arg|_], St0) -> {Str,St1} = luerl_lib:tostring(Arg, St0), {[Str],St1}; tostring(_, As, St) -> badarg_error(tostring, As, St). %% type([Data|_], State) -> {Type,State}. %% Return the type of the argument. type(_, [Arg|_], St) -> {[type(Arg)],St}; %Only one return value! type(_, As, St) -> badarg_error(type, As, St). type(nil) -> <<"nil">>; type(N) when is_number(N) -> <<"number">>; type(S) when is_binary(S) -> <<"string">>; type(B) when is_boolean(B) -> <<"boolean">>; type(#tref{}) -> <<"table">>; type(#usdref{}) -> <<"userdata">>; type(#funref{}) -> <<"function">>; %Functions defined in Lua type(#erl_func{}) -> <<"function">>; %Internal functions type(#erl_mfa{}) -> <<"function">>; type(#thread{}) -> <<"thread">>; type(_) -> <<"unknown">>. %% getmetatable([Value|_], State) -> {Table,State}. %% setmetatable([Table,Table|nil|_], State) -> {Table,State}. %% Can only set the metatable of tables here. Return tables for all %% values, for tables and userdata it is the table of the object, %% else the metatable for the type. getmetatable(_, [Obj|_], St) -> case luerl_heap:get_metatable(Obj, St) of #tref{}=Meta -> #table{d=Dict} = luerl_heap:get_table(Meta, St), case ttdict:find(<<"__metatable">>, Dict) of {ok,MM} -> {[MM],St}; error -> {[Meta],St} end; nil -> {[nil],St} end; getmetatable(_, As, St) -> badarg_error(getmetatable, As, St). setmetatable(_, [#tref{}=T,#tref{}=M|_], St) -> do_setmetatable(T, M, St); setmetatable(_, [#tref{}=T,nil|_], St) -> do_setmetatable(T, nil, St); setmetatable(_, As, St) -> badarg_error(setmetatable, As, St). do_setmetatable(#tref{}=Tref, Meta, St0) -> case luerl_heap:get_metamethod(Tref, <<"__metatable">>, St0) of nil -> Upd = fun (Tab) -> Tab#table{meta=Meta} end, St1 = luerl_heap:upd_table(Tref, Upd, St0), {[Tref],St1}; _ -> badarg_error(setmetatable, [Tref], St0) end. %% Do files. dofile(_, As, St) -> case luerl_lib:conv_list(As, [erl_string]) of [File] -> %% Compile the file so it returns errors. Ret = luerl_comp:file(File, [verbose,return]), dofile_ret(Ret, As, St); _ -> badarg_error(dofile, As, St) end. dofile_ret({ok,Chunk}, _, St0) -> {Func,St1} = luerl_emul:load_chunk(Chunk, St0), luerl_emul:call(Func, [], St1); dofile_ret({error,[{_,Mod,E}|_],_}, _As, St) -> Msg = unicode:characters_to_binary(Mod:format_error(E)), lua_error({error_message,Msg}, St). %% Load string and files. load(_, As, St) -> case luerl_lib:conv_list(As, [erl_string,lua_string,lua_string,lua_any]) of [S|_] -> %% Compile the string so it returns errors. Ret = luerl_comp:string(S, [verbose,return]), load_ret(Ret, St); error -> badarg_error(load, As, St) end. loadfile(_, As, St) -> case luerl_lib:conv_list(As, [erl_string,lua_string,lua_any]) of [F|_] -> %% Compile the file so it returns errors. Ret = luerl_comp:file(F, [verbose,return]), load_ret(Ret, St); error -> badarg_error(loadfile, As, St) end. loadstring(_, As, St) -> case luerl_lib:conv_list(As, [erl_string]) of [S] -> %% Compile the string so it returns errors. Ret = luerl_comp:string(S, [verbose,return]), load_ret(Ret, St); error -> badarg_error(loadstring, As, St) end. load_ret({ok,Chunk}, St0) -> {Func,St1} = luerl_emul:load_chunk(Chunk, St0), {[Func],St1}; load_ret({error,[{_,Mod,E}|_],_}, St) -> Msg = unicode:characters_to_binary(Mod:format_error(E)), {[nil,Msg],St}. pcall(_, [F|As], St0) -> try {Rs,St1} = luerl_emul:functioncall(F, As, St0), {[true|Rs],St1} catch %% Only catch Lua errors here, signal system errors. error:{lua_error,{error_call, Eas},St2} -> Msg = case Eas of [E|_] -> {Str,_} = luerl_lib:tostring(E, St2), Str; [] -> <<"nil">> end, {[false,Msg],St2}; error:{lua_error,E,St2} -> Msg = luerl_lib:format_error(E), {[false,Msg],St2} end. %% Lua 5.1 compatibility functions. unpack(_, As, St) -> luerl_lib_table:unpack(As, St). rvirding-luerl-772b25e/src/luerl_lib_math.erl0000664000232200023220000002734715114130670021656 0ustar debalancedebalance%% Copyright (c) 2013-2020 Robert Virding %% %% 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. %% File : luerl_lib_math.erl %% Author : Robert Virding %% Purpose : The math library for Luerl. %% We try to mirror the handling of arguments which occurs in the Lua %% math module. Many functions allow extra arguments but only look at %% the first required ones of the right type and completely ignore the %% rest. %% %% We keep atan2, cosh, sinh tanh, pow, frexp and ldexp even though %% have been deprecated. -module(luerl_lib_math). -include("luerl.hrl"). ?MODULEDOC(false). -export([install/1,abs/3,acos/3,asin/3,atan2/3,atan/3,ceil/3,cos/3,cosh/3,deg/3,exp/3,floor/3, fmod/3,frexp/3,ldexp/3,log/3,log10/3,max/3,min/3,modf/3,pow/3,rad/3,random/3,randomseed/3, sin/3,sinh/3,sqrt/3,tan/3,tanh/3,tointeger/3,type/3]). -export([internalize/1,externalize/1]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this %% Use the correct random number module. -ifdef(NEW_RAND). -define(RAND_UNIFORM(S), rand:uniform_s(S)). -define(RAND_UNIFORM(L, S), rand:uniform_s(L, S)). -define(RAND_SEED(), rand:seed_s(exs1024)). -define(RAND_SEED(S1,S2,S3), rand:seed_s(exs1024, {S1,S2,S3})). -define(RAND_EXTERNALIZE(S), rand_externalize(S)). -define(RAND_INTERNALIZE(S), rand_internalize(S)). -else. -define(RAND_UNIFORM(S), random:uniform_s(S)). -define(RAND_UNIFORM(L, S), random:uniform_s(L, S)). -define(RAND_SEED(), random:seed0()). -define(RAND_SEED(S1,S2,S3), %Naughty, copied from source {(abs(S1) rem (30269-1) + 1), %PRIME1 (abs(S2) rem (30307-1) + 1), %PRIME2 (abs(S3) rem (30323-1) + 1)}). %PRIME3 -define(RAND_EXTERNALIZE(S), S). % random has just three integers for state so no special work needed. -define(RAND_INTERNALIZE(S), S). -endif. install(St0) -> St1 = St0#luerl{rand=?RAND_SEED()}, %Default initial random seed luerl_heap:alloc_table(table(), St1). table() -> [{<<"abs">>,#erl_mfa{m=?MODULE,f=abs}}, {<<"acos">>,#erl_mfa{m=?MODULE,f=acos}}, {<<"asin">>,#erl_mfa{m=?MODULE,f=asin}}, {<<"atan">>,#erl_mfa{m=?MODULE,f=atan}}, {<<"atan2">>,#erl_mfa{m=?MODULE,f=atan2}}, %For 5.2 backwards compatibility {<<"ceil">>,#erl_mfa{m=?MODULE,f=ceil}}, {<<"cos">>,#erl_mfa{m=?MODULE,f=cos}}, {<<"cosh">>,#erl_mfa{m=?MODULE,f=cosh}}, %For 5.2 backwards compatibility {<<"deg">>,#erl_mfa{m=?MODULE,f=deg}}, {<<"exp">>,#erl_mfa{m=?MODULE,f=exp}}, {<<"floor">>,#erl_mfa{m=?MODULE,f=floor}}, {<<"fmod">>,#erl_mfa{m=?MODULE,f=fmod}}, {<<"frexp">>,#erl_mfa{m=?MODULE,f=frexp}}, %For 5.2 backwards compatibility {<<"huge">>,1.7976931348623157e308}, %From the specs {<<"ldexp">>,#erl_mfa{m=?MODULE,f=ldexp}}, %For 5.2 backwards compatibility {<<"log">>,#erl_mfa{m=?MODULE,f=log}}, {<<"log10">>,#erl_mfa{m=?MODULE,f=log10}}, %For 5.1 backwards compatibility {<<"max">>,#erl_mfa{m=?MODULE,f=max}}, {<<"maxinteger">>,16#7FFFFFFFFFFFFFFF}, %From Lua 5.4.3 {<<"min">>,#erl_mfa{m=?MODULE,f=min}}, {<<"mininteger">>,-16#8000000000000000}, %From Lua 5.4.3 {<<"modf">>,#erl_mfa{m=?MODULE,f=modf}}, {<<"pi">>,math:pi()}, {<<"pow">>,#erl_mfa{m=?MODULE,f=pow}}, {<<"rad">>,#erl_mfa{m=?MODULE,f=rad}}, {<<"random">>,#erl_mfa{m=?MODULE,f=random}}, {<<"randomseed">>,#erl_mfa{m=?MODULE,f=randomseed}}, {<<"sin">>,#erl_mfa{m=?MODULE,f=sin}}, {<<"sinh">>,#erl_mfa{m=?MODULE,f=sinh}}, %For 5.2 backwards compatibility {<<"sqrt">>,#erl_mfa{m=?MODULE,f=sqrt}}, {<<"tan">>,#erl_mfa{m=?MODULE,f=tan}}, {<<"tanh">>,#erl_mfa{m=?MODULE,f=tanh}}, %For 5.2 backwards compatibility {<<"tointeger">>,#erl_mfa{m=?MODULE,f=tointeger}}, {<<"type">>,#erl_mfa{m=?MODULE,f=type}} ]. %% abs(Args, State) -> {[Ret],State}. abs(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[abs(N)],St}; _ -> badarg_error(abs, As, St) end. acos(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:acos(N)],St}; _ -> badarg_error(acos, As, St) end. asin(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:asin(N)],St}; _ -> badarg_error(asin, As, St) end. atan(_, As, St) -> case get_number_args(As) of [N1,N2|_] when is_number(N1), is_number(N2) -> {[math:atan2(N1, N2)],St}; [N|_] when is_number(N) -> {[math:atan(N)],St}; _ -> badarg_error(atan, As, St) end. atan2(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [N1,N2|_] when is_number(N1), is_number(N2) -> {[math:atan2(N1, N2)],St}; _ -> badarg_error(atan2, As, St) end. ceil(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[ceil(N)],St}; _ -> badarg_error(ceil, As, St) end. -ifndef(HAS_CEIL). %% ceil(Number) -> integer(). %% Ceil does not exist before 20 so we need to do it ourselves. ceil(N) when is_integer(N) -> N; ceil(N) when is_float(N) -> round(N + 0.5). -endif. cos(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:cos(N)],St}; _ -> badarg_error(cos, As, St) end. cosh(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [N|_] when is_number(N) -> {[math:cosh(N)],St}; _ -> badarg_error(cosh, As, St) end. deg(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[180.0*N/math:pi()],St}; _ -> badarg_error(deg, As, St) end. exp(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:exp(N)],St}; _ -> badarg_error(exp, As, St) end. floor(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[floor(N)],St}; _ -> badarg_error(floor, As, St) end. -ifndef(HAS_FLOOR). %% floor(Number) -> integer(). %% Floor does not exist before 20 so we need to do it ourselves. floor(N) when is_integer(N) -> N; floor(N) when is_float(N) -> round(N - 0.5). -endif. fmod(_, As, St) -> case get_number_args(As) of [X,Y|_] when is_number(X), is_number(Y) -> Div = trunc(X/Y), Rem = X - Div*Y, {[Rem],St}; _ -> badarg_error(fmod, As, St) end. frexp(_, As, St) -> %For 5.2 backwards compatibility %% M,E such that X = M * 2 ^ E. case get_number_args(As) of [X|_] when is_number(X) -> %% The sneaky bit! <> = <<(X+0.0)/float>>, Two52 = 1 bsl 52, Man1 = (Man0 bor Two52)/Two52, %% Bit naughty here, export Man2, Exp1 if Man1 >= 1.0 -> Man2 = Man1/2, Exp1 = Exp0 - 1022; Man1 < 0.5 -> Man2 = Man1*2.0, Exp1 = Exp0 - 1024; true -> Man2 = Man1, Exp1 = Exp0 - 1023 end, Ret = if Sign =:= 1 -> -Man2; true -> Man2 end, {[float(Ret),Exp1],St}; _ -> badarg_error(frexp, As, St) end. ldexp(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [M,E|_] when is_float(M), is_integer(E) -> {[M*math:pow(2, E)],St}; %% <> = <<0:1,E:11,M:52>>, %% {[X],St}; _ -> badarg_error(ldexp, As, St) end. log(_, As, St) -> case get_number_args(As) of [N1,N2|_] when is_number(N1), N2 == 10 -> {[math:log10(N1)],St}; %Seeing it is builtin [N1,N2|_] when is_number(N1), is_number(N2) -> {[math:log(N1)/math:log(N2)],St}; [N|_] when is_number(N) -> {[math:log(N)],St}; _ -> badarg_error(log, As, St) end. log10(_, As, St) -> %For 5.1 backwards compatibility case get_number_args(As) of [N|_] when N == 0 -> {[-500.0],St}; %Bit hacky [N|_] when is_number(N) -> {[math:log10(N)],St}; _ -> badarg_error(log10, As, St) end. max(_, As, St) -> case luerl_lib:args_to_numbers(As) of [_|_]=Ns -> {[lists:max(Ns)],St}; %At least one number _ -> badarg_error(max, As, St) end. min(_, As, St) -> case luerl_lib:args_to_numbers(As) of [_|_]=Ns -> {[lists:min(Ns)],St}; %At least one number _ -> badarg_error(min, As, St) end. modf(_, As, St) -> case get_number_args(As) of [N|_] when is_integer(N) -> {[N,0.0],St}; [N|_] when is_float(N) -> I = trunc(N), %Integral part {[I,float(N-I)],St}; _ -> badarg_error(modf, As, St) end. pow(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [N1,N2|_] when is_number(N1) and is_number(N2) -> {[math:pow(N1, N2)],St}; _ -> badarg_error(pow, As, St) end. rad(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:pi()*N/180.0],St}; _ -> badarg_error(rad, As, St) end. random(_, As, #luerl{rand=S0}=St) -> case luerl_lib:args_to_integers(As) of [] -> %0.0 - 1.0 {R,S1} = ?RAND_UNIFORM(S0), {[R],St#luerl{rand=S1}}; [M] when M >= 1 -> {R,S1} = ?RAND_UNIFORM(M, S0), {[R],St#luerl{rand=S1}}; [M,N] when N >= M -> {R,S1} = ?RAND_UNIFORM(N - M + 1, S0), {[R + M - 1],St#luerl{rand=S1}}; _ -> badarg_error(random, As, St) end. randomseed(_, As, St) -> case get_number_args(As) of [S|_] when is_number(S) -> %% Split integer or float-64 into three integers. <> = <>, {[],St#luerl{rand=?RAND_SEED(A1, A2, A3)}}; _ -> badarg_error(randomseed, As, St) end. sin(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:sin(N)],St}; _ -> badarg_error(sin, As, St) end. sinh(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [N|_] when is_number(N) -> {[math:sinh(N)],St}; _ -> badarg_error(sinh, As, St) end. sqrt(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:sqrt(N)],St}; _ -> badarg_error(sqrt, As, St) end. tan(_, As, St) -> case get_number_args(As) of [N|_] when is_number(N) -> {[math:tan(N)],St}; _ -> badarg_error(tan, As, St) end. tanh(_, As, St) -> %For 5.2 backwards compatibility case get_number_args(As) of [N|_] when is_number(N) -> {[math:tanh(N)],St}; _ -> badarg_error(tanh, As, St) end. tointeger(_, As, St) -> case get_number_args(As) of [N|_] when is_integer(N) -> {[N],St}; [N|_] when is_float(N) -> case trunc(N) of I when I == N -> {[I],St}; _ -> {[nil], St} end; [_|_] -> {[nil],St}; [] -> badarg_error(tointeger, As, St) end. type(_, As, St) -> %% No conversion here. case As of [N|_] when is_integer(N) -> {[<<"integer">>],St}; [N|_] when is_float(N) -> {[<<"float">>],St}; [_|_] -> {[nil],St}; %Not a number [] -> badarg_error(type, As, St) end. %% get_number_args(Args) -> [Number]. %% Convert args to numbers inserting nil when not possible. This %% allows us to disting between no numbers and an empty list. get_number_args(As) -> lists:map(fun luerl_lib:arg_to_number/1, As). %% get_number_args([A|As]) -> %% case luerl_lib:tonumber(A) of %% N when is_number(N) -> %% [N|get_number_args(As)]; %% nil -> [] %% end; %% get_number_args([]) -> []. internalize(S) -> ?RAND_INTERNALIZE(S). externalize(S) -> ?RAND_EXTERNALIZE(S). -ifdef(NEW_RAND). rand_externalize(#luerl{rand=S0}=St) -> St#luerl{rand=rand:export_seed_s(S0)}. rand_internalize(#luerl{rand=S0}=St) -> St#luerl{rand=rand:seed_s(S0)}. -endif. rvirding-luerl-772b25e/src/luerl_lib_table.erl0000664000232200023220000004656715114130670022021 0ustar debalancedebalance%% Copyright (c) 2013-2020 Robert Virding %% %% 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. %% File : luerl_lib_table.erl %% Author : Robert Virding %% Purpose : The table library for Luerl. %% These functions sometimes behave strangely in the Lua 5.2 %% libraries, but we try to follow them. Most of these functions KNOW %% that a table is a ttdict! We know that the erlang array has default %% value 'nil'. -module(luerl_lib_table). -include("luerl.hrl"). ?MODULEDOC(false). %% The basic entry point to set up the function table. -export([install/1,concat/3,insert/3,pack/3,remove/3,sort/3,unpack/3]). %% Export some functions which can be called from elsewhere. -export([concat/4,concat/5,raw_length/2,length/2,unpack/2]). %% Export some test functions. -export([test_concat/1, test_insert/2,test_insert/3, test_remove/2,test_remove/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this install(St) -> luerl_heap:alloc_table(table(), St). %% table() -> [{FuncName,Function}]. table() -> [{<<"concat">>,#erl_mfa{m=?MODULE,f=concat}}, {<<"insert">>,#erl_mfa{m=?MODULE,f=insert}}, {<<"pack">>,#erl_mfa{m=?MODULE,f=pack}}, {<<"remove">>,#erl_mfa{m=?MODULE,f=remove}}, {<<"sort">>,#erl_mfa{m=?MODULE,f=sort}}, {<<"unpack">>,#erl_mfa{m=?MODULE,f=unpack}} ]. %% concat - concat the elements of a list into a string. concat(_, As, St0) -> try do_concat(As, St0) catch throw:{error,E,St1} -> lua_error(E, St1); throw:{error,E} -> lua_error(E, St0) end. do_concat([#tref{}=Tref|As], St) -> #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St), case luerl_lib:conv_list(concat_args(As), [lua_string,lua_integer,lua_integer]) of [Sep,I] -> {[do_concat(Arr, Dict, Sep, I, length_loop(Arr))],St}; [Sep,I,J] -> {[do_concat(Arr, Dict, Sep, I, J)],St}; _ -> throw({error,{badarg,concat,As},St}) end; do_concat(As, St) -> throw({error,{badarg,concat,As},St}). %% concat(Table, Sep, I, State) -> string(). %% concat(Table, Sep, I, J, State) -> string(). %% Concatenate elements in a list into a string. Callable from %% Erlang. concat(Tref, Sep, I, St) -> #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St), J = length_loop(Arr), do_concat(Arr, Dict, Sep, I, J). concat(Tref, Sep, I, J, St) -> #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St), do_concat(Arr, Dict, Sep, I, J). test_concat(As) -> concat_args(As). concat_args([]) -> concat_args([<<>>]); concat_args([nil|As]) -> concat_args([<<>>|As]); concat_args([Sep]) -> [Sep,1.0]; concat_args([Sep,nil|As]) -> concat_args([Sep,1.0|As]); concat_args([Sep,I]) -> [Sep,I]; concat_args([Sep,I,nil|_]) -> [Sep,I]; concat_args([Sep,I,J|_]) -> [Sep,I,J]. do_concat(Arr, Dict, Sep, I, J) -> Conc = concat_table(Arr, Dict, I, J), concat_join(Conc, Sep). concat_table(Arr, Dict, I, J) -> concat_tab(Arr, Dict, I, J). %% This and unpack_loop are very similar. %% First scan over table up to 0 then the array. We have the indexes %% and limits as integers and explicitly use '==' to compare with %% float values in table. concat_tab(_, _, N, J) when N > J -> []; %Done concat_tab(Arr, _, N, J) when N > 0 -> %Done with table concat_arr(Arr, N, J); concat_tab(Arr, Dict, N, J) -> case ttdict:find(N, Dict) of {ok,V} -> case luerl_lib:arg_to_list(V) of error -> throw({error,{illegal_value,concat,V}}); S -> [S|concat_tab(Arr, Dict, N+1, J)] end; error -> throw({error,{illegal_value,concat,nil}}) end. concat_arr(_, N, J) when N > J -> []; concat_arr(Arr, N, J) -> V = array:get(N, Arr), case luerl_lib:arg_to_list(V) of error -> throw({error,{illegal_value,concat,V}}); S -> [S|concat_arr(Arr, N+1, J)] end. concat_join([E], _) -> list_to_binary(E); concat_join([E1|Es], Sep) -> iolist_to_binary([E1|[ [Sep,E] || E <- Es ]]); concat_join([], _) -> <<>>. %% insert(Table, [Pos,] Value) -> [] %% Insert an element into a list shifting following elements. insert(_, [Tref,V], St) when ?IS_TREF(Tref) -> #table{a=Arr0} = T = luerl_heap:get_table(Tref, St), Arr1 = do_insert_last(Arr0, V), {[],luerl_heap:set_table(Tref, T#table{a=Arr1}, St)}; insert(_, [Tref,P0,V]=As, St) when ?IS_TREF(Tref) -> #table{a=Arr0} = T = luerl_heap:get_table(Tref, St), Size = length_loop(Arr0), case luerl_lib:arg_to_integer(P0) of P1 when P1 >=1, P1 =< Size+1 -> Arr1 = do_insert(Arr0, P1, V), {[],luerl_heap:set_table(Tref, T#table{a=Arr1}, St)}; _ -> badarg_error(insert, As, St) end; insert(_, As, St) -> badarg_error(insert, As, St). test_insert(A, V) -> do_insert_last(A, V). test_insert(A, N, V) -> do_insert(A, N, V). %% do_insert_last(Array, V) -> Array. %% Get the "length" of the first bit and put value in first slot %% after that. do_insert_last(Arr, V) -> Len = length_loop(Arr), %Get "length" array:set(Len+1, V, Arr). %Set the value %% do_insert(Array, P, V) -> Array. %% We only insert elements inside the "proper" 1..n table. do_insert(Arr, P, V) -> %Go to the array part insert_array(Arr, P, V). insert_array(Arr0, N, Here) -> %Put this at N shifting up case array:get(N, Arr0) of nil -> array:set(N, Here, Arr0); %Just fill hole Next -> %Take value for next slot Arr1 = array:set(N, Here, Arr0), insert_array(Arr1, N+1, Next) end. %% remove(Table [,Pos]) -> Value. %% Remove an element from a list shifting following elements. remove(_, [Tref], St) when ?IS_TREF(Tref) -> #table{a=Arr0,d=Dict0} = T = luerl_heap:get_table(Tref, St), {Ret,Arr1,Dict1} = do_remove_last(Arr0, Dict0), {Ret,luerl_heap:set_table(Tref, T#table{a=Arr1,d=Dict1}, St)}; remove(_, [Tref,P0|_]=As, St) when ?IS_TREF(Tref) -> #table{a=Arr0,d=Dict0} = T = luerl_heap:get_table(Tref, St), case luerl_lib:arg_to_integer(P0) of P1 when P1 =/= nil -> case do_remove(Arr0, Dict0, P1) of {Ret,Arr1,Dict1} -> {Ret, luerl_heap:set_table(Tref, T#table{a=Arr1,d=Dict1}, St)}; badarg -> badarg_error(remove, As, St) end; _ -> badarg_error(remove, As, St) %nil or P < 1 end; remove(_, As, St) -> badarg_error(remove, As, St). test_remove(Arr, Dict) -> do_remove_last(Arr, Dict). test_remove(Arr, Dict, N) -> do_remove(Arr, Dict, N). %% do_remove_last(Array, Dict) -> {Return,Array,Dict}. %% Find the length and remove the last element. Return it even if it %% is nil. do_remove_last(Arr0, Dict0) -> case length_loop(Arr0) of 0 -> do_remove_0(Arr0, Dict0); Size -> Val = array:get(Size, Arr0), Arr1 = array:set(Size, nil, Arr0), {[Val],Arr1,Dict0} end. do_remove_0(Arr, Dict0) -> case ttdict:find(0.0, Dict0) of {ok,Val} -> Dict1 = ttdict:erase(0.0, Dict0), {[Val],Arr,Dict1}; error -> {[nil],Arr,Dict0} end. %% do_remove(Array, Dict, P) -> {Return,Array,Dict} | badarg. %% Don't ask, it tries to emulate the "real" Lua, where we can't %% remove elements elements outside of the "proper" 1..n dict. do_remove(Arr, Dict, P) -> do_remove(Arr, Dict, P, length_loop(Arr)). do_remove(Arr, Dict, 0, 0) -> do_remove_0(Arr, Dict); do_remove(Arr, Dict, 1, 0) -> {[nil],Arr,Dict}; do_remove(Arr0, Dict, P, Size) when P >= 1, P =< Size+1 -> Ret = array:get(P, Arr0), Arr1 = remove_array_1(Arr0, P), {[Ret],Arr1,Dict}; do_remove(_, _, _, _) -> badarg. remove_array_1(Arr0, N) -> There = array:get(N+1, Arr0), %Next value Arr1 = array:set(N, There, Arr0), if There =:= nil -> Arr1; %End if next a nil true -> remove_array_1(Arr1, N+1) end. %% pack - pack arguments in to a table. pack(_, As, St0) -> T = pack_loop(As, 0), %Indexes are integers! {Tab,St1} = luerl_heap:alloc_table(T, St0), {[Tab],St1}. pack_loop([E|Es], N) -> [{N+1,E}|pack_loop(Es, N+1)]; pack_loop([], N) -> [{<<"n">>,N}]. %% unpack - unpack table into return values. unpack(_, [#tref{}=Tref|As], St) -> #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St), case luerl_lib:args_to_integers(unpack_args(As)) of [I] -> Unp = do_unpack(Arr, Dict, I, length_loop(Arr)), %% io:fwrite("unp: ~p\n", [{Arr,I,Start,Unp}]), {Unp,St}; [I,J] -> Unp = do_unpack(Arr, Dict, I, J), %% io:fwrite("unp: ~p\n", [{Arr,I,J,Start,Unp}]), {Unp,St}; error -> %Not numbers badarg_error(unpack, [Tref|As], St) end; unpack(_, [], St) -> badarg_error(unpack, [], St). unpack(As, St) -> unpack(nil, As, St). %% unpack_args(Args) -> Args. %% Fix args for unpack getting defaults right and handling 'nil'. unpack_args([]) -> unpack_args([1.0]); %Just start from the beginning unpack_args([nil|As]) -> unpack_args([1.0|As]); unpack_args([I]) -> [I]; %Only one argument unpack_args([I,nil|_]) -> [I]; %Goto the default end unpack_args([I,J|_]) -> [I,J]. %Only use two arguments %% This and concat_table are very similar. %% First scan over table up to 0 then the array. We have the indexes %% and limits as integers and explicitly use '==' to compare with %% float values in table. do_unpack(Arr, Dict, I, J) -> unpack_tab(Arr, Dict, I, J). unpack_tab(_, _, N, J) when N > J -> []; %Done unpack_tab(Arr, _, N, J) when N > 0 -> %Done with table unpack_arr(Arr, N, J); unpack_tab(Arr, Dict, N, J) -> E = case ttdict:find(N, Dict) of {ok,V} -> V; error -> nil end, [E|unpack_tab(Arr, Dict, N+1, J)]. unpack_arr(_, N, J) when N > J -> []; unpack_arr(Arr, N, J) -> [array:get(N, Arr)|unpack_arr(Arr, N+1, J)]. %% length(Table, State) -> {Length,State}. %% raw_length(Table, State) -> Length. %% The length of a table is the number of numeric keys in sequence %% from 1. Except if 1 is nil followed by non-nil. Don't ask! length(#tref{}=T, St0) -> Meta = luerl_heap:get_metamethod(T, <<"__len">>, St0), if ?IS_TRUE(Meta) -> {Ret,St1} = luerl_emul:functioncall(Meta, [T], St0), {luerl_lib:first_value(Ret),St1}; true -> {raw_length(T, St0),St0} end. raw_length(Tref, St) -> #table{a=Arr} = luerl_heap:get_table(Tref, St), length_loop(Arr). length_loop(Arr) -> case {array:get(1, Arr),array:get(2, Arr)} of {nil,nil} -> 0; {nil,_} -> length_loop(3, Arr); {_,nil} -> 1; {_,_} -> length_loop(3, Arr) end. length_loop(I, Arr) -> case array:get(I, Arr) of nil -> I-1; _ -> length_loop(I+1, Arr) end. %% sort(Table [,SortFun]) %% Sort the elements of the list after their values. sort(_, [Tref], St0) when ?IS_TREF(Tref) -> Comp = fun (A, B, St) -> lt_comp(A, B, St) end, St1 = do_sort(Comp, St0, Tref), {[],St1}; sort(_, [Tref,Func|_], St0) when ?IS_TREF(Tref) -> Comp = fun (A, B, St) -> luerl_emul:functioncall(Func, [A,B], St) end, St1 = do_sort(Comp, St0, Tref), {[],St1}; sort(_, As, St) -> badarg_error(sort, As, St). do_sort(Comp, St0, Tref) -> #table{a=Arr0} = T = luerl_heap:get_table(Tref, St0), case array:to_list(Arr0) of [] -> St0; %Nothing to do [E0|Es0] -> %% 1st element index 0, skip it and then prepend it again {Es1,St1} = merge_sort(Comp, St0, Es0), Arr2 = array:from_list([E0|Es1], nil), %% io:fwrite("so: ~p\n", [{Arr0,Arr1,Arr2}]), luerl_heap:set_table(Tref, T#table{a=Arr2}, St1) end. %% lt_comp(O1, O2, State) -> {[Bool],State}. %% Proper Lua '<' comparison. lt_comp(O1, O2, St) when is_number(O1), is_number(O2) -> {[O1 =< O2],St}; lt_comp(O1, O2, St) when is_binary(O1), is_binary(O2) -> {[O1 =< O2],St}; lt_comp(O1, O2, St0) -> case luerl_heap:get_metamethod(O1, O2, <<"__lt">>, St0) of nil -> lua_error({illegal_comp,sort}, St0); Meta -> {Ret,St1} = luerl_emul:functioncall(Meta, [O1,O2], St0), {[luerl_lib:boolean_value(Ret)],St1} end. %% sort(A,B,C) -> sort_up(A,B,C). %% sort_up(A,B,[X,Y|L]) -> %% case X =< Y of %% true -> merge_dn([Y,X], sort_dn(A, B, L), []); %% false -> merge_dn([X,Y], sort_dn(A, B, L), []) %% end; %% sort_up(A,B,[X]) -> [X]; %% sort_up(A,B,[]) -> []. %% sort_dn(A,B,[X,Y|L]) -> %% case X =< Y of %% true -> merge_up([X,Y], sort_up(A, B, L), []); %% false -> merge_up([Y,X], sort_up(A, B, L), []) %% end; %% sort_dn(A,B,[X]) -> [X]; %% sort_dn(A,B,[]) -> []. %% merge(A,B,C) -> %% merge_dn(A,B,C). %% %% merge_up(L1, L2, Acc) %% %% L1, L2 increasing, Acc will be decreasing %% merge_up([X|Xs]=Xs0, [Y|Ys]=Ys0, Acc) -> %% case X =< Y of %% true -> merge_up(Xs, Ys0, [X|Acc]); %% false -> merge_up(Xs0, Ys, [Y|Acc]) %% end; %% merge_up([X|Xs], [], Acc) -> merge_up(Xs, [], [X|Acc]); %% merge_up([], [Y|Ys], Acc) -> merge_up([], Ys, [Y|Acc]); %% merge_up([], [], Acc) -> Acc. %% %% merge_dn(L1, L2, Acc) %% %% L1, L2 decreasing, Acc will be increasing %% merge_dn([X|Xs]=Xs0, [Y|Ys]=Ys0, Acc) -> %% case X =< Y of %% true -> merge_dn(Xs0, Ys, [Y|Acc]); %% false -> merge_dn(Xs, Ys0, [X|Acc]) %% end; %% merge_dn([X|Xs], [], Acc) -> merge_dn(Xs, [], [X|Acc]); %% merge_dn([], [Y|Ys], Acc) -> merge_dn([], Ys, [Y|Acc]); %% merge_dn([], [], Acc) -> Acc. %% merge_sort(CompFun, State, List) -> {SortedList,State}. %% The code here has been taken from the sort/2 code in lists.erl and %% converted to chain State through all calls to the comparison %% function. merge_sort(_, St, []) -> {[],St}; merge_sort(_, St, [_] = L) -> {L,St}; merge_sort(Fun, St0, [X, Y|T]) -> {Ret,St1} = Fun(X, Y, St0), case luerl_lib:boolean_value(Ret) of true -> fsplit_1(Y, X, Fun, St1, T, [], []); false -> fsplit_2(Y, X, Fun, St1, T, [], []) end. %% Ascending. fsplit_1(Y, X, Fun, St0, [Z|L], R, Rs) -> {Ret1,St1} = Fun(Y, Z, St0), case luerl_lib:boolean_value(Ret1) of true -> fsplit_1(Z, Y, Fun, St1, L, [X|R], Rs); false -> {Ret2,St2} = Fun(X, Z, St1), case luerl_lib:boolean_value(Ret2) of true -> fsplit_1(Y, Z, Fun, St2, L, [X|R], Rs); false when R == [] -> fsplit_1(Y, X, Fun, St2, L, [Z], Rs); false -> fsplit_1_1(Y, X, Fun, St2, L, R, Rs, Z) end end; fsplit_1(Y, X, Fun, St, [], R, Rs) -> rfmergel([[Y, X|R]|Rs], [], Fun, St, asc). fsplit_1_1(Y, X, Fun, St0, [Z|L], R, Rs, S) -> {Ret1,St1} = Fun(Y, Z, St0), case luerl_lib:boolean_value(Ret1) of true -> fsplit_1_1(Z, Y, Fun, St1, L, [X|R], Rs, S); false -> {Ret2,St2} = Fun(X, Z, St1), case luerl_lib:boolean_value(Ret2) of true -> fsplit_1_1(Y, Z, Fun, St2, L, [X|R], Rs, S); false -> {Ret3,St3} = Fun(S, Z, St2), case luerl_lib:boolean_value(Ret3) of true -> fsplit_1(Z, S, Fun, St3, L, [], [[Y, X|R]|Rs]); false -> fsplit_1(S, Z, Fun, St3, L, [], [[Y, X|R]|Rs]) end end end; fsplit_1_1(Y, X, Fun, St, [], R, Rs, S) -> rfmergel([[S], [Y, X|R]|Rs], [], Fun, St, asc). %% Descending. fsplit_2(Y, X, Fun, St0, [Z|L], R, Rs) -> {Ret1,St1} = Fun(Y, Z, St0), case luerl_lib:boolean_value(Ret1) of false -> fsplit_2(Z, Y, Fun, St1, L, [X|R], Rs); true -> {Ret2,St2} = Fun(X, Z, St1), case luerl_lib:boolean_value(Ret2) of false -> fsplit_2(Y, Z, Fun, St2, L, [X|R], Rs); true when R == [] -> fsplit_2(Y, X, Fun, St2, L, [Z], Rs); true -> fsplit_2_1(Y, X, Fun, St2, L, R, Rs, Z) end end; fsplit_2(Y, X, Fun, St, [], R, Rs) -> fmergel([[Y, X|R]|Rs], [], Fun, St, desc). fsplit_2_1(Y, X, Fun, St0, [Z|L], R, Rs, S) -> {Ret1,St1} = Fun(Y, Z, St0), case luerl_lib:boolean_value(Ret1) of false -> fsplit_2_1(Z, Y, Fun, St1, L, [X|R], Rs, S); true -> {Ret2,St2} = Fun(X, Z, St1), case luerl_lib:boolean_value(Ret2) of false -> fsplit_2_1(Y, Z, Fun, St2, L, [X|R], Rs, S); true -> {Ret3,St3} = Fun(S, Z, St2), case luerl_lib:boolean_value(Ret3) of false -> fsplit_2(Z, S, Fun, St3, L, [], [[Y, X|R]|Rs]); true -> fsplit_2(S, Z, Fun, St3, L, [], [[Y, X|R]|Rs]) end end end; fsplit_2_1(Y, X, Fun, St, [], R, Rs, S) -> fmergel([[S], [Y, X|R]|Rs], [], Fun, St, desc). fmergel([T1, [H2|T2]|L], Acc, Fun, St0, asc) -> {L1,St1} = fmerge2_1(T1, H2, Fun, St0, T2, []), fmergel(L, [L1|Acc], Fun, St1, asc); fmergel([[H2|T2], T1|L], Acc, Fun, St0, desc) -> {L1,St1} = fmerge2_1(T1, H2, Fun, St0, T2, []), fmergel(L, [L1|Acc], Fun, St1, desc); fmergel([L], [], _Fun, St, _O) -> {L,St}; fmergel([L], Acc, Fun, St, O) -> rfmergel([lists:reverse(L, [])|Acc], [], Fun, St, O); fmergel([], Acc, Fun, St, O) -> rfmergel(Acc, [], Fun, St, O). rfmergel([[H2|T2], T1|L], Acc, Fun, St0, asc) -> {L1,St1} = rfmerge2_1(T1, H2, Fun, St0, T2, []), rfmergel(L, [L1|Acc], Fun, St1, asc); rfmergel([T1, [H2|T2]|L], Acc, Fun, St0, desc) -> {L1,St1} = rfmerge2_1(T1, H2, Fun, St0, T2, []), rfmergel(L, [L1|Acc], Fun, St1, desc); rfmergel([L], Acc, Fun, St, O) -> fmergel([lists:reverse(L, [])|Acc], [], Fun, St, O); rfmergel([], Acc, Fun, St, O) -> fmergel(Acc, [], Fun, St, O). %% merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); %% merge(Fun, T1, []) when is_function(Fun, 2) -> %% T1. %% Elements from the first list are prioritized. fmerge2_1([H1|T1], H2, Fun, St0, T2, M) -> {Ret,St1} = Fun(H1, H2, St0), case luerl_lib:boolean_value(Ret) of true -> fmerge2_1(T1, H2, Fun, St1, T2, [H1|M]); false -> fmerge2_2(H1, T1, Fun, St1, T2, [H2|M]) end; fmerge2_1([], H2, _Fun, St, T2, M) -> {lists:reverse(T2, [H2|M]),St}. fmerge2_2(H1, T1, Fun, St0, [H2|T2], M) -> {Ret,St1} = Fun(H1, H2, St0), case luerl_lib:boolean_value(Ret) of true -> fmerge2_1(T1, H2, Fun, St1, T2, [H1|M]); false -> fmerge2_2(H1, T1, Fun, St1, T2, [H2|M]) end; fmerge2_2(H1, T1, _Fun, St, [], M) -> {lists:reverse(T1, [H1|M]),St}. %% rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% lists:reverse(rfmerge2_1(T1, H2, Fun, T2, []), []); %% rmerge(Fun, T1, []) when is_function(Fun, 2) -> %% T1. rfmerge2_1([H1|T1], H2, Fun, St0, T2, M) -> {Ret,St1} = Fun(H1, H2, St0), case luerl_lib:boolean_value(Ret) of true -> rfmerge2_2(H1, T1, Fun, St1, T2, [H2|M]); false -> rfmerge2_1(T1, H2, Fun, St1, T2, [H1|M]) end; rfmerge2_1([], H2, _Fun, St, T2, M) -> {lists:reverse(T2, [H2|M]),St}. rfmerge2_2(H1, T1, Fun, St0, [H2|T2], M) -> {Ret,St1} = Fun(H1, H2, St0), case luerl_lib:boolean_value(Ret) of true -> rfmerge2_2(H1, T1, Fun, St1, T2, [H2|M]); false -> rfmerge2_1(T1, H2, Fun, St1, T2, [H1|M]) end; rfmerge2_2(H1, T1, _Fun, St, [], M) -> {lists:reverse(T1, [H1|M]),St}. rvirding-luerl-772b25e/src/ttdict.erl0000664000232200023220000005537415114130670020170 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : ttdict.erl %% Author : Robert Virding %% Purpose : Key-Value dictionary as a 2-3 tree. %% This implementation uses 2-3 trees. The description of the tree %% restructuring which is used comes from Prof. Lyn Turbak's notes for %% CS230 Data Structures at Wellesley College. -module(ttdict). -include("luerl.hrl"). ?MODULEDOC(false). %% Standard interface. -export([new/0,is_key/2,to_list/1,from_list/1,size/1]). -export([fetch/2,find/2,fetch_keys/1,erase/2]). -export([store/3,append/3,append_list/3]). -export([update_val/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). %% Extended interface. -export([foreach/2,mapfold/3]). %% Special interface. -export([first/1,last/1,next/2,prev/2]). %% Deprecated interface. -export([dict_to_list/1,list_to_dict/1]). -deprecated([{dict_to_list,1},{list_to_dict,1}]). -compile({no_auto_import,[size/1]}). %We mean our own size/1 -ifdef(DEBUG). -export([check_depth/1]). -endif. %% Data structure: %% - {Left,Key,Val,Right} %% - {Left,Key,Val,Middle,Key,Val,Right} %% - empty %% %% The term order is an arithmetic total order, so we should not %% test exact equality for the keys. (If we do, then it becomes %% possible that neither `>', `<', nor `=:=' matches.) Testing '<' %% and '>' first is statistically better than testing for %% equality, and also allows us to skip the test completely in the %% remaining case. %% %% In all the functions we test keys from left to right in the %% structure. This might not always be the best choice (is there any %% best choice?) but it makes the code consistent. -type ttdict() :: empty | {ttdict(),any(),any(),ttdict()} | {ttdict(),any(),any(),ttdict(),any(),any(),ttdict()}. -export_type([ttdict/0]). -spec new() -> Dict when Dict :: ttdict(). new() -> empty. %The empty dict -spec is_key(Key, Dict) -> boolean() when Key :: term(), Dict :: ttdict(). is_key(_, empty) -> false; is_key(Key, {L,Xk,_,_}) when Key < Xk -> is_key(Key, L); is_key(Key, {_,Xk,_,R}) when Key > Xk -> is_key(Key, R); is_key(_, {_,_,_,_}) -> true; %Key == Xk is_key(Key, {L,Xk,_,_,_,_,_}) when Key < Xk -> is_key(Key, L); is_key(Key, {_,Xk,_,M,Yk,_,R}) when Key > Xk -> if Key < Yk -> is_key(Key, M); Key > Yk -> is_key(Key, R); true -> true %Key == Yk end; is_key(_, {_,_,_,_,_,_,_}) -> true. %Key == Xk -spec to_list(Dict) -> List when Dict :: ttdict(), List :: [{Key :: term(),Value :: term()}]. to_list(D) -> to_list(D, []). to_list(empty, Tail) -> Tail; to_list({L,Xk,Xv,R}, Tail) -> to_list(L, [{Xk,Xv}|to_list(R, Tail)]); to_list({L,Xk,Xv,M,Yk,Yv,R}, Tail) -> to_list(L, [{Xk,Xv}|to_list(M, [{Yk,Yv}|to_list(R, Tail)])]). -spec from_list(List) -> Dict when List :: [{Key :: term(),Value :: term()}], Dict :: ttdict(). from_list(List) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), List). -spec size(Dict) -> non_neg_integer() when Dict :: ttdict(). size(empty) -> 0; size({L,_,_,R}) -> size(L) + size(R) + 1; size({L,_,_,M,_,_,R}) -> size(L) + size(M) + size(R) + 2. -spec fetch(Key, Dict) -> Value when Key :: term(), Dict :: ttdict(), Value :: term(). fetch(K, {L,Xk,_,_}) when K < Xk -> fetch(K, L); fetch(K, {_,Xk,_,R}) when K > Xk -> fetch(K, R); fetch(_, {_,_,Xv,_}) -> Xv; fetch(K, {L,Xk,_,_,_,_,_}) when K < Xk -> fetch(K, L); fetch(K, {_,Xk,_,M,Yk,Yv,R}) when K > Xk -> if K < Yk -> fetch(K, M); %Middle K > Yk -> fetch(K, R); %Right true -> Yv end; fetch(_, {_,_,Xv,_,_,_,_}) -> Xv; fetch(_, empty) -> error(badarg). -spec find(Key, Dict) -> {ok,Value} | error when Key :: term(), Dict :: ttdict(), Value :: term(). find(K, {L,Xk,_,_}) when K < Xk -> find(K, L); find(K, {_,Xk,_,B}) when K > Xk -> find(K, B); find(_, {_,_,Xv,_}) -> {ok,Xv}; find(K, {L,Xk,_,_,_,_,_}) when K < Xk -> find(K, L); find(K, {_,Xk,_,M,Yk,Yv,R}) when K > Xk -> if K < Yk -> find(K, M); %Middle K > Yk -> find(K, R); %Right true -> {ok,Yv} end; find(_, {_,_,Xv,_,_,_,_}) -> {ok,Xv}; find(_, empty) -> error. -spec fetch_keys(Dict) -> Keys when Dict :: ttdict(), Keys :: [term()]. fetch_keys(D) -> fetch_keys(D, []). fetch_keys(empty, Tail) -> Tail; fetch_keys({L,Xk,_,R}, Tail) -> fetch_keys(L, [Xk|fetch_keys(R, Tail)]); fetch_keys({L,Xk,_,M,Yk,_,R}, Tail) -> fetch_keys(L, [Xk|fetch_keys(M, [Yk|fetch_keys(R, Tail)])]). -spec store(Key, Value, Dict1) -> Dict2 when Key :: term(), Value :: term(), Dict1 :: ttdict(), Dict2 :: ttdict(). store(Key, Val, T) -> %% Store and check for a returned "Up" node. case store_aux(Key, Val, T) of {up,Lu,Ku,Vu,Ru} -> {Lu,Ku,Vu,Ru}; Node -> Node end. store_aux(Key, Val, empty) -> {up,empty,Key,Val,empty}; %"Up" node store_aux(Key, Val, {empty,K,V,empty}) -> %% Special case to avoid creating temporary "up" nodes. %% It helps a little bit, but not much. if Key < K -> {empty,Key,Val,empty,K,V,empty}; Key > K -> {empty,K,V,empty,Key,Val,empty}; true -> {empty,K,Val,empty} end; store_aux(Key, Val, {L,K,V,R}) -> if Key < K -> %Down the left store_up2_l(store_aux(Key, Val, L), K, V, R); Key > K -> %Down the right store_up2_r(L, K, V, store_aux(Key, Val, R)); true -> {L,K,Val,R} %Replace current value end; store_aux(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> store_up3_l(store_aux(Key, Val, L), Xk, Xv, M, Yk, Yv, R); store_aux(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> %Down the middle store_up3_m(L, Xk, Xv, store_aux(Key, Val, M), Yk, Yv, R); Key > Yk -> %Down the right store_up3_r(L, Xk, Xv, M, Yk, Yv, store_aux(Key, Val, R)); true -> {L,Xk,Xv,M,Yk,Val,R} end; store_aux(_, Val, {L,Xk,_,M,Yk,Yv,R}) -> %Key == Xk {L,Xk,Val,M,Yk,Yv,R}. -spec append(Key, Value, Dict1) -> Dict2 when Key :: term(), Value :: term(), Dict1 :: ttdict(), Dict2 :: ttdict(). append(Key, Val, T) -> %% Append and check for a returned "Up" node. case append_aux(Key, [Val], T) of {up,Lu,Ku,Vu,Ru} -> {Lu,Ku,Vu,Ru}; Node -> Node end. -spec append_list(Key, Values, Dict1) -> Dict2 when Key :: term(), Values :: [Value :: term()], Dict1 :: ttdict(), Dict2 :: ttdict(). append_list(Key, Val, T) -> %% Append and check for a returned "Up" node. case append_aux(Key, Val, T) of {up,Lu,Ku,Vu,Ru} -> {Lu,Ku,Vu,Ru}; Node -> Node end. append_aux(Key, Val, empty) -> {up,empty,Key,Val,empty}; %"Up" node append_aux(Key, Val, {L,K,V,R}) -> if Key < K -> %Down the left store_up2_l(append_aux(Key, Val, L), K, V, R); Key > K -> %Down the right store_up2_r(L, K, V, append_aux(Key, Val, R)); true -> {L,Key,V ++ Val,R} %Append to current value end; append_aux(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> store_up3_l(append_aux(Key, Val, L), Xk, Xv, M, Yk, Yv, R); append_aux(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> store_up3_m(L, Xk, Xv, append_aux(Key, Val, M), Yk, Yv, R); Key > Yk -> store_up3_r(L, Xk, Xv, M, Yk, Yv, append_aux(Key, Val, R)); true -> {L,Xk,Xv,M,Key,Yv ++ Val,R} end; append_aux(Key, Val, {L,_,Xv,M,Yk,Yv,R}) -> {L,Key,Xv ++ Val,M,Yk,Yv,R}. -spec update_val(Key, Value, Dict1) -> Dict2 when Key :: term(), Value :: term(), Dict1 :: ttdict(), Dict2 :: ttdict(). update_val(Key, Val, {L,Xk,Xv,R}) -> if Key < Xk -> {update_val(Key, Val, L),Xk,Xv,R}; Key > Xk -> {L,Xk,Xv,update_val(Key, Val, R)}; true -> {L,Xk,Val,R} end; update_val(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> {update_val(Key, Val, L),Xk,Xv,M,Yk,Yv,R}; update_val(Key, Val, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> {L,Xk,Xv,update_val(Key, Val, M),Yk,Yv,R}; Key > Yk -> {L,Xk,Xv,M,Yk,Yv,update_val(Key, Val, R)}; true -> {L,Xk,Xv,M,Yk,Val,R} end; update_val(_, Val, {L,Xk,_,M,Yk,Yv,R}) -> %Key == Xk {L,Xk,Val,M,Yk,Yv,R}. -spec update(Key, Fun, Dict1) -> Dict2 when Key :: term(), Fun :: fun((Value1 :: term()) -> Value2 :: term()), Dict1 :: ttdict(), Dict2 :: ttdict(). update(Key, Fun, {L,Xk,Xv,R}) -> if Key < Xk -> {update(Key, Fun, L),Xk,Xv,R}; Key > Xk -> {L,Xk,Xv,update(Key, Fun, R)}; true -> {L,Xk,Fun(Xv),R} %Key == Xk end; update(Key, Fun, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> {update(Key, Fun, L),Xk,Xv,M,Yk,Yv,R}; update(Key, Fun, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> {L,Xk,Xv,update(Key, Fun, M),Yk,Yv,R}; Key > Yk -> {L,Xk,Xv,M,Yk,Yv,update(Key, Fun, R)}; true -> {L,Xk,Xv,M,Yk,Fun(Yv),R} end; update(_, Fun, {L,Xk,Xv,M,Yk,Yv,R}) -> %Key == Xk {L,Xk,Fun(Xv),M,Yk,Yv,R}. -spec update(Key, Fun, Initial, Dict1) -> Dict2 when Key :: term, Initial :: term(), Fun :: fun((Value :: term()) -> Value2 :: term()), Dict1 :: ttdict(), Dict2 :: ttdict(). update(Key, Fun, I, T) -> case update_aux(Key, Fun, I, T) of {up,Lu,Ku,Vu,Ru} -> {Lu,Ku,Vu,Ru}; Node -> Node end. update_aux(Key, _, I, empty) -> {up,empty,Key,I,empty}; update_aux(Key, Fun, I, {L,Xk,Xv,R}) -> if Key < Xk -> store_up2_l(update_aux(Key, Fun, I, L), Xk, Xv, R); Key > Xk -> store_up2_r(L, Xk, Xv, update_aux(Key, Fun, I, R)); true -> {L,Xk,Fun(Xv),R} end; update_aux(Key, Fun, I, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> store_up3_l(update_aux(Key, Fun, I, L), Xk, Xv, M, Yk, Yv, R); update_aux(Key, Fun, I, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> store_up3_m(L, Xk, Xv, update_aux(Key, Fun, I, M), Yk, Yv, R); Key > Yk -> store_up3_r(L, Xk, Xv, M, Yk, Yv, update_aux(Key, Fun, I, R)); true -> {L,Xk,Xv,M,Key,Fun(Yv),R} end; update_aux(_, Fun, _, {L,Xk,Xv,M,Yk,Yv,R}) -> %Key == Xk {L,Xk,Fun(Xv),M,Yk,Yv,R}. -spec update_counter(Key, Increment, Dict1) -> Dict2 when Key :: term(), Increment :: number(), Dict1 :: ttdict(), Dict2 :: ttdict(). update_counter(Key, I, T) -> case update_counter_aux(Key, I, T) of {up,Lu,Ku,Vu,Ru} -> {Lu,Ku,Vu,Ru}; Node -> Node end. update_counter_aux(Key, I, empty) -> {up,empty,Key,I,empty}; update_counter_aux(Key, I, {L,Xk,Xv,R}) -> if Key < Xk -> store_up2_l(update_counter_aux(Key, I, L), Xk, Xv, R); Key > Xk -> store_up2_r(L, Xk, Xv, update_counter_aux(Key, I, R)); true -> {L,Xk,Xv+I,R} end; update_counter_aux(Key, I, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> store_up3_l(update_counter_aux(Key, I, L), Xk, Xv, M, Yk, Yv, R); update_counter_aux(Key, I, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> store_up3_m(L, Xk, Xv, update_counter_aux(Key, I, M), Yk, Yv, R); Key > Yk -> store_up3_r(L, Xk, Xv, M, Yk, Yv, update_counter_aux(Key, I, R)); true -> {L,Xk,Xv,M,Yk,Yv+I,R} end; update_counter_aux(_, I, {L,Xk,Xv,B,Yk,Yv,R}) -> %Key == Xk {L,Xk,Xv+I,B,Yk,Yv,R}. %% store_up2_l/r(L, K, V, R) -> {L,Xk,Xv,M,Yk,Yv,R} | {L,K,V,R}. store_up2_l({up,Lu,Ku,Vu,Ru}, K, V, R) -> {Lu,Ku,Vu,Ru,K,V,R}; store_up2_l(L, K, V, R) -> {L,K,V,R}. store_up2_r(L, K, V, {up,Lu,Ku,Vu,Ru}) -> {L,K,V,Lu,Ku,Vu,Ru}; store_up2_r(L, K, V, R) -> {L,K,V,R}. %% store_up3_l/m/r(L, Xk, Xv, M, Yk, Yv, R) -> %% {up,L,K,V,R} | {L,Xk,Xv,M,Yk,Yv,R}. store_up3_l({up,Lu,Ku,Vu,Ru}, Xk, Xv, M, Yk, Yv, R) -> {up,{Lu,Ku,Vu,Ru},Xk,Xv,{M,Yk,Yv,R}}; store_up3_l(L, Xk, Xv, M, Yk, Yv, R) -> {L,Xk,Xv,M,Yk,Yv,R}. store_up3_m(L, Xk, Xv, {up,Lu,Ku,Vu,Ru}, Yk, Yv, R) -> {up,{L,Xk,Xv,Lu},Ku,Vu,{Ru,Yk,Yv,R}}; store_up3_m(L, Xk, Xv, M, Yk, Yv, R) -> {L,Xk,Xv,M,Yk,Yv,R}. store_up3_r(L, Xk, Xv, M, Yk, Yv, {up,Lu,Ku,Vu,Ru}) -> {up,{L,Xk,Xv,M},Yk,Yv,{Lu,Ku,Vu,Ru}}; store_up3_r(L, Xk, Xv, M, Yk, Yv, R) -> {L,Xk,Xv,M,Yk,Yv,R}. -spec erase(Key, Dict1) -> Dict2 when Key :: term(), Dict1 :: ttdict(), Dict2 :: ttdict(). erase(Key, T) -> case erase_aux(Key, T) of {up,T1} -> T1; %??? T1 -> T1 end. erase_aux(_, empty) -> empty; %No element erase_aux(Key, {empty,Xk,_,empty}=N) -> if Key < Xk; Key > Xk -> N; %No element true -> {up,empty} end; erase_aux(Key, {L,Xk,Xv,R}) -> if Key < Xk -> %Down the left erase_up2_l(erase_aux(Key, L), Xk, Xv, R); Key > Xk -> %Down the right erase_up2_r(L, Xk, Xv, erase_aux(Key, R)); true -> {{Km,Vm},R1}= erase_min(R), erase_up2_r(L, Km, Vm, R1) end; erase_aux(Key, {empty,Xk,Xv,empty,Yk,Yv,empty}=N) -> if Key < Xk -> N; %No element Key > Xk -> if Key < Yk -> N; %No element Key > Yk -> N; true -> {empty,Xk,Xv,empty} end; true -> {empty,Yk,Yv,empty} end; erase_aux(Key, {L,Xk,Xv,M,Yk,Yv,R}) when Key < Xk -> erase_up3_l(erase_aux(Key, L), Xk, Xv, M, Yk, Yv, R); erase_aux(Key, {L,Xk,Xv,M,Yk,Yv,R}) when Key > Xk -> if Key < Yk -> erase_up3_m(L, Xk, Xv, erase_aux(Key, M), Yk, Yv, R); Key > Yk -> erase_up3_r(L, Xk, Xv, M, Yk, Yv, erase_aux(Key, R)); true -> {{Km,Vm},R1} = erase_min(R), erase_up3_r(L, Xk, Xv, M, Km, Vm, R1) end; erase_aux(_, {L,_,_,M,Yk,Yv,R}) -> {{Km,Vm},M1} = erase_min(M), erase_up3_m(L, Km, Vm, M1, Yk, Yv, R). erase_min(T) -> %%io:format("em: ~p\n-> ~p\n", [T,T1]), erase_min1(T). erase_min1({empty,Xk,Xv,empty}) -> {{Xk,Xv},{up,empty}}; erase_min1({L,Xk,Xv,R}) -> {Min,L1} = erase_min1(L), {Min,erase_up2_l(L1, Xk, Xv, R)}; erase_min1({empty,Xk,Xv,empty,Yk,Yv,empty}) -> {{Xk,Xv},{empty,Yk,Yv,empty}}; erase_min1({L,Xk,Xv,M,Yk,Yv,R}) -> {Min,L1} = erase_min1(L), {Min,erase_up3_l(L1, Xk, Xv, M, Yk, Yv, R)}. %% erase_up2_l/r(L, K, V, R) -> Node | {up,Node}. %% We use the same naming of nodes and keys as in the text. It makes %% checking the rules easier. erase_up2_l({up,L}, Xk, Xv, {M,Yk,Yv,R}) -> %1.1 {up,{L,Xk,Xv,M,Yk,Yv,R}}; erase_up2_l({up,A}, Xk, Xv, {B,Yk,Yv,C,Zk,Zv,D}) -> %2.1 {{A,Xk,Xv,B},Yk,Yv,{C,Zk,Zv,D}}; erase_up2_l(L, K, V, R) -> {L,K,V,R}. erase_up2_r({L,Xk,Xv,M}, Yk, Yv, {up,R}) -> %1.2 {up,{L,Xk,Xv,M,Yk,Yv,R}}; erase_up2_r({A,Xk,Xv,B,Yk,Yv,C}, Zk, Zv, {up,D}) -> %2.2 {{A,Xk,Xv,B},Yk,Yv,{C,Zk,Zv,D}}; erase_up2_r(L, K, V, R) -> {L,K,V,R}. %% erase_up2_r(L, K, V, {up,R}) -> erase_up2_r1(L, K, V, R); %% erase_up2_r(L, K, V, R) -> {L,K,V,R}. %% erase_up2_r1({L,Xk,Xv,M}, Yk, Yv, R) -> %1.2 %% {up,{L,Xk,Xv,M,Yk,Yv,R}}; %% erase_up2_r1({A,Xk,Xv,B,Yk,Yv,C}, Zk, Zv, D) -> %2.2 %% {{A,Xk,Xv,B},Yk,Yv,{C,Zk,Zv,D}}. %% erase_up3_l/m/r(L, Xk, Xv, M, Yk, Yv, R) -> Node | {up,Node}. %% We use the same naming of nodes and keys as in the text. It makes %% checking the rules easier. N.B. there are alternate valid choices %% for the middle case! erase_up3_l({up,A}, Xk, Xv, {B,Yk,Yv,C}, Zk, Zv, D) -> %3a.1 {{A,Xk,Xv,B,Yk,Yv,C},Zk,Zv,D}; erase_up3_l({up,A}, Wk, Wv, {B,Xk,Xv,C,Yk,Yv,D}, Zk, Zv, E) -> %4a.1 {{A,Wk,Wv,B},Xk,Xv,{C,Yk,Yv,D},Zk,Zv,E}; erase_up3_l(A, Xk, Xv, B, Yk, Yv, C) -> {A,Xk,Xv,B,Yk,Yv,C}. erase_up3_m({A,Xk,Xv,B}, Yk, Yv, {up,C}, Zk, Zv, D) -> %3a.2 {{A,Xk,Xv,B,Yk,Yv,C},Zk,Zv,D}; erase_up3_m(A, Xk, Xv, {up,B}, Yk, Yv, {C,Zk,Zv,D}) -> %3b.1 {A,Xk,Xv,{B,Yk,Yv,C,Zk,Zv,D}}; erase_up3_m({A,Wk,Wv,B,Xk,Xv,C}, Yk, Yv, {up,D}, Zk, Zv, E) -> %4a.2 {{A,Wk,Wv,B},Xk,Xv,{C,Yk,Yv,D},Zk,Zv,E}; erase_up3_m(A, Wk, Wv, {up,B}, Xk, Xv, {C,Yk,Yv,D,Zk,Zv,E}) -> %4b.1 {A,Wk,Wv,{B,Xk,Xv,C},Yk,Yv,{D,Zk,Zv,E}}; erase_up3_m(A, Xk, Xv, B, Yk, Yv, C) -> {A,Xk,Xv,B,Yk,Yv,C}. erase_up3_r(A, Xk, Xv, {B,Yk,Yv,C}, Zk, Zv, {up,D}) -> %3b.2 {A,Xk,Xv,{B,Yk,Yv,C,Zk,Zv,D}}; erase_up3_r(A, Wk, Wv, {B,Xk,Xv,C,Yk,Yv,D}, Zk, Zv, {up,E}) -> %4b.2 {A,Wk,Wv,{B,Xk,Xv,C},Yk,Yv,{D,Zk,Zv,E}}; erase_up3_r(A, Xk, Xv, B, Yk, Yv, C) -> {A,Xk,Xv,B,Yk,Yv,C}. -spec fold(Fun, Acc0, Dict) -> Acc1 when Fun :: fun((Key, Value, AccIn) -> AccOut), Key :: term(), Value :: term(), Acc0 :: term(), Acc1 :: term(), AccIn :: term(), AccOut :: term(), Dict :: ttdict(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. fold(_, Acc, empty) -> Acc; fold(F, Acc, {L,Xk,Xv,R}) -> fold(F, F(Xk, Xv, fold(F, Acc, R)), L); fold(F, Acc, {L,Xk,Xv,M,Yk,Yv,R}) -> fold(F, F(Xk, Xv, fold(F, F(Yk, Yv, fold(F, Acc, R)), M)), L). -spec map(Fun, Dict1) -> Dict2 when Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), Dict1 :: ttdict(), Dict2 :: ttdict(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. map(_, empty) -> empty; map(F, {A,Xk,Xv,B}) -> {map(F, A),Xk,F(Xk, Xv),map(F, B)}; map(F, {A,Xk,Xv,B,Yk,Yv,C}) -> {map(F, A),Xk,F(Xk, Xv),map(F, B),Yk,F(Yk, Yv),map(F, C)}. -spec filter(Pred, Dict1) -> Dict2 when Pred :: fun((Key :: term(), Value :: term()) -> boolean()), Dict1 :: ttdict(), Dict2 :: ttdict(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. filter(F, D) -> filter(F, D, new()). filter(_, empty, New) -> New; filter(F, {L,Xk,Xv,R}, New0) -> New1 = filter(F, L, New0), New2 = case F(Xk, Xv) of true -> store(Xk, Xv, New1); false -> New1 end, filter(F, R, New2); filter(F, {L,Xk,Xv,M,Yk,Yv,R}, New0) -> New1 = filter(F, L, New0), New2 = case F(Xk, Xv) of true -> store(Xk, Xv, New1); false -> New1 end, New3 = filter(F, M, New2), New4 = case F(Yk, Yv) of true -> store(Yk, Yv, New3); false -> New3 end, filter(F, R, New4). -spec merge(Fun, Dict1, Dict2) -> Dict3 when Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), Dict1 :: ttdict(), Dict2 :: ttdict(), Dict3 :: ttdict(). merge(F, D1, D2) -> fold(fun (K, V2, D) -> update(K, fun(V1) -> F(K, V1, V2) end, V2, D) end, D1, D2). %% Extended interface. -spec foreach(Fun, Dict) -> ok when Fun :: fun((Key :: term(), Value :: term()) -> term()), Dict :: ttdict(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. foreach(_, empty) -> ok; foreach(F, {L,Xk,Xv,R}) -> foreach(F, L), F(Xk, Xv), foreach(F, R); foreach(F, {L,Xk,Xv,M,Yk,Yv,R}) -> foreach(F, L), F(Xk, Xv), foreach(F, M), F(Yk, Yv), foreach(F, R). -spec mapfold(Fun, Acc0, Dict1) -> {Dict2,Acc1} when Fun :: fun((Key, Value1, AccIn) -> {Value2,AccOut}), Acc0 :: term(), Acc1 :: term(), Key :: term(), Value1 :: term(), Value2 :: term(), AccIn :: term(), AccOut :: term(), Dict1 :: ttdict(), Dict2 :: ttdict(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. mapfold(_, Acc, empty) -> {empty,Acc}; mapfold(F, Acc0, {L0,Xk,Xv0,R0}) -> {L1,Acc1} = mapfold(F, Acc0, L0), {Xv1,Acc2} = F(Xk, Xv0, Acc1), {R1,Acc3} = mapfold(F, Acc2, R0), {{L1,Xk,Xv1,R1},Acc3}; mapfold(F, Acc0, {L0,Xk,Xv0,M0,Yk,Yv0,R0}) -> {L1,Acc1} = mapfold(F, Acc0, L0), {Xv1,Acc2} = F(Xk, Xv0, Acc1), {M1,Acc3} = mapfold(F, Acc2, M0), {Yv1,Acc4} = F(Yk, Yv0, Acc3), {R1,Acc5} = mapfold(F, Acc4, R0), {{L1,Xk,Xv1,M1,Yk,Yv1,R1},Acc5}. %% Special interface. -spec first(Dict) -> error | {ok,{Key1,Value}} when Key1 :: term(), Value :: term(), Dict :: ttdict(). first(empty) -> error; first({L,Xk,Xv,_}) -> case first(L) of error -> {ok,{Xk,Xv}}; First -> First end; first({L,Xk,Xv,_,_,_,_}) -> case first(L) of error -> {ok,{Xk,Xv}}; First -> First end. -spec last(Dict) -> error | {ok,{Key1,Value}} when Key1 :: term(), Value :: term(), Dict :: ttdict(). last(empty) -> error; last({_,Xk,Xv,R}) -> case last(R) of error -> {ok,{Xk,Xv}}; Last -> Last end; last({_,_,_,_,Yk,Yv,R}) -> case last(R) of error -> {ok,{Yk,Yv}}; Last -> Last end. -spec next(Key, Dict) -> error | {ok,{Key1,Value}} when Key :: term(), Key1 :: term(), Value :: term(), Dict :: ttdict(). next(_, empty) -> error; next(K, {L,Xk,Xv,_}) when K < Xk -> case next(K, L) of error -> {ok,{Xk,Xv}}; Next -> Next end; next(K, {_,Xk,_,R}) when K > Xk -> next(K, R); next(_, {_,_,_,R}) -> first(R); %when K == Xk next(K, {L,Xk,Xv,_,_,_,_}) when K < Xk -> case next(K, L) of error -> {ok,{Xk,Xv}}; Next -> Next end; next(K, {_,Xk,_,M,Yk,Yv,R}) when K > Xk -> if K < Yk -> case next(K, M) of error -> {ok,{Yk,Yv}}; Next -> Next end; K > Yk -> next(K, R); true -> first(R) %when K == Yk end; next(_, {_,_,_,M,Yk,Yv,_}) -> %when K == Xk case first(M) of error -> {ok,{Yk,Yv}}; First -> First end. -spec prev(Key, Dict) -> error | {ok,{Key1,Value}} when Key :: term(), Key1 :: term(), Value :: term(), Dict :: ttdict(). %% Go from right to left here as it makes it easier to understand %% what is going on. prev(_, empty) -> error; prev(K, {_,Xk,Xv,R}) when K > Xk -> case prev(K, R) of error -> {ok,{Xk,Xv}}; Prev -> Prev end; prev(K, {L,Xk,_,_}) when K < Xk -> prev(K, L); prev(_, {L,_,_,_}) -> last(L); %when K == Xk prev(K, {_,_,_,_,Yk,Yv,R}) when K > Yk -> case prev(K, R) of error -> {ok,{Yk,Yv}}; Prev -> Prev end; prev(K, {L,Xk,Xv,M,Yk,_,_}) when K < Yk -> if K > Xk -> case prev(K, M) of error -> {ok,{Xk,Xv}}; Prev -> Prev end; K < Xk -> prev(K, L); true -> last(L) %when K == Xk end; prev(_, {_,Xk,Xv,M,_,_,_}) -> %when K == Yk case last(M) of error -> {ok,{Xk,Xv}}; Prev -> Prev end. %% Deprecated interface. %% dict_to_list(Dictionary) -> [{Key,Value}]. dict_to_list(D) -> to_list(D). %% list_to_dict([{Key,Value}]) -> Dictionary. list_to_dict(L) -> from_list(L). -ifdef(DEBUG). %% Check the depth of all the leaves, should all be the same. check_depth(T) -> check_depth(T, 1, orddict:new()). check_depth(empty, D, Dd) -> orddict:update_counter(D, 1, Dd); check_depth({L,_,_,R}, D, Dd0) -> Dd1 = orddict:update_counter(two, 1, Dd0), Dd2 = check_depth(L, D+1, Dd1), check_depth(R, D+1, Dd2); check_depth({L,_,_,M,_,_,R}, D, Dd0) -> Dd1 = orddict:update_counter(three, 1, Dd0), Dd2 = check_depth(L, D+1, Dd1), Dd3 = check_depth(M, D+1, Dd2), check_depth(R, D+1, Dd3). -endif. rvirding-luerl-772b25e/src/luerl_comp_normalise.erl0000664000232200023220000003332115114130670023073 0ustar debalancedebalance%% Copyright (c) 2019 Robert Virding %% %% 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. %% File : luerl_comp_normalise.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Here we normalise the code and convert it to an internal form. -module(luerl_comp_normalise). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). -export([chunk/2]). %% chunk(Code, CompInfo) -> {ok,Code} | {error,Reason}. chunk(Code0, #cinfo{opts=Opts}=Ci0) -> %% The chunk is a function. {Code1,_Ci1} = functiondef(Code0, Ci0), luerl_comp:debug_print(Opts, "cn: ~p\n", [Code1]), {ok,Code1}. stmts([{local,L,{functiondef,Lf,Name,Pars,Block}}|Ss], St) -> %% Need to split this up to handle references to Name in the function. Fdef = {functiondef,Lf,Pars,Block}, stmts([{local, L, {assign, L, [Name], [{nil,L}]}}, {';',L}, {local, Lf, {assign, Lf, [Name], [Fdef]}} | Ss], St); stmts([{';',_}|Ss], St) -> stmts(Ss, St); %No-op so we drop it stmts([S0|Ss0], St0) -> {S1,St1} = stmt(S0, St0), {Ss1,St2} = stmts(Ss0, St1), {[S1|Ss1],St2}; stmts([], St) -> {[],St}. %% stmt(Statement, State) -> {CStat,State}. %% Do a statement. The ';' statement will caught and removed in stmts/2. stmt({assign,Line,Vs,Es}, St) -> assign_stmt(Line, Vs, Es, St); stmt({return,Line,Es}, St) -> return_stmt(Line, Es, St); stmt({break,L}, St) -> %Interesting {#break_stmt{l=L},St}; stmt({block,Line,B}, St) -> block_stmt(Line, B, St); stmt({while,Line,Exp,B}, St) -> while_stmt(Line, Exp, B, St); stmt({repeat,Line,B,Exp}, St) -> repeat_stmt(Line, B, Exp, St); stmt({'if',Line,Tests,Else}, St) -> if_stmt(Line, Tests, Else, St); stmt({for,Line,V,I,L,B}, St) -> %Default step of 1 numfor_stmt(Line, V, I, L, {'NUMERAL',Line,1}, B, St); stmt({for,Line,V,I,L,S,B}, St) -> numfor_stmt(Line, V, I, L, S, B, St); stmt({for,Line,Ns,Gs,B}, St) -> genfor_stmt(Line, Ns, Gs, B, St); stmt({functiondef,Line,Fname,Ps,B}, St) -> fdef_stmt(Line, Fname, Ps, B, St); stmt({local,Line,Local}, St) -> local_stmt(Line, Local, St); stmt(Exp, St) -> %This is really just a call Line = element(2, Exp), call_stmt(Line, Exp, St). %% assign_stmt(Line, Vars, Exps, State) -> {Assign,State}. assign_stmt(Line, Vs, Es, St0) -> {Ces,St1} = explist(Es, St0), {Cvs,St2} = assign_loop(Vs, St1), Anno = line_file_anno(Line, St2), {#assign_stmt{l=Anno,vars=Cvs,exps=Ces},St2}. assign_loop([V|Vs], St0) -> {Cv,St1} = var(V, St0), {Cvs,St2} = assign_loop(Vs, St1), {[Cv|Cvs],St2}; assign_loop([], St) -> {[],St}. %% var(VarExp, State) -> {VarExp,State}. %% Step down the prefixexp sequence evaluating as we go, stop at the %% END and return a key and a table where to put data. This is a %% prefixexp with different tail. Attributes are only allowed in %% local assigns and for now we just ignore them. var({'.',L,Exp,Rest}, St0) -> {Ce,St1} = prefixexp_first(Exp, St0), {Cr,St2} = var_rest(Rest, St1), {dot(L, Ce, Cr),St2}; var({{'NAME',L,N},_Attribute}, St) -> %% For now we ignore attributes. {var_name(L, N),St}; var({'NAME',L,N}, St) -> {var_name(L, N),St}. var_rest({'.',L,Exp,Rest}, St0) -> {Ce,St1} = prefixexp_element(Exp, St0), {Cr,St2} = var_rest(Rest, St1), {dot(L, Ce, Cr),St2}; var_rest(Exp, St) -> var_last(Exp, St). var_last({'NAME',L,N}, St) -> %% Transform this to a key_field with the name string. NO! {#key{l=L,key=lit_name(L, N)},St}; var_last({key_field,L,Exp}, St0) -> {Ce,St1} = exp(Exp, St0), {#key{l=L,key=Ce},St1}. %% call_stmt(Line, Exp, State) -> {Call,State}. call_stmt(Line, Exp, St0) -> {Ce,St1} = exp(Exp, St0), Anno = line_file_anno(Line, St1), {#call_stmt{l=Anno,call=Ce},St1}. %% return_stmt(Line, Exps, State) -> {Return,State}. return_stmt(Line, Es, St0) -> {Ces,St1} = explist(Es, St0), Anno = line_file_anno(Line, St1), {#return_stmt{l=Anno,exps=Ces},St1}. %% block_stmt(Line, Stats, State) -> {Block,Stmte}. block_stmt(Line, Ss0, St0) -> {Ss1,St1} = stmts(Ss0, St0), Anno = line_file_anno(Line, St1), {#block_stmt{l=Anno,body=Ss1},St1}. block(Line, Ss0, St0) -> {Ss1,St1} = stmts(Ss0, St0), {#block{l=Line,body=Ss1},St1}. %% while_stmt(Line, Exp, Block, State) -> {While,State}. while_stmt(Line, Exp, B, St0) -> {Ce,St1} = exp(Exp, St0), {Cb,St2} = block(Line, B, St1), Anno = line_file_anno(Line, St2), {#while_stmt{l=Anno,exp=Ce,body=Cb},St2}. %% repeat_stmt(Line, Block, Exp, State) -> {Repeat,State}. %% Append the test expression into the block as a single value %% expression. repeat_stmt(Line, B, Exp, St0) -> {Cb0,St1} = block(Line, B, St0), {Ce,St2} = expr_stmt(Line, {single,Line,Exp}, St1), Cb1 = Cb0#block{body=Cb0#block.body ++ [Ce]}, Anno = line_file_anno(Line, St2), {#repeat_stmt{l=Anno,body=Cb1},St2}. %% if_stmt(Line, Test, Else, State) -> {If,State}. if_stmt(Line, Tests, Else, St0) -> {Cts,St1} = if_tests(Line, Tests, St0), {Ce,St2} = block(Line, Else, St1), Anno = line_file_anno(Line, St2), {#if_stmt{l=Anno,tests=Cts,else_block=Ce},St2}. if_tests(L, Ts, St) -> Test = fun ({T,B}, S0) -> {Ct,S1} = exp(T, S0), {Cb,S2} = block(L, B, S1), {{Ct,Cb},S2} end, lists:mapfoldl(Test, St, Ts). %% numfor_stmt(Line, Var, Init, Limit, Step, Stmts, State) -> {NumFor,State}. numfor_stmt(Line, {'NAME',Ln,N}, I0, L0, S0, Ss, St0) -> Var = var_name(Ln, N), {[I1,L1,S1],St1} = explist([I0,L0,S0], St0), {B,St2} = block(Line, Ss, St1), Anno = line_file_anno(Line, St2), {#nfor_stmt{l=Anno,var=Var,init=I1,limit=L1,step=S1,body=B},St2}. %% genfor_stmt(Line, Vars, Generators, Stmts, State) -> {GenFor,State}. genfor_stmt(Line, Vs0, Gs0, Ss, St0) -> Vs1 = [ var_name(Ln, N) || {'NAME',Ln,N} <- Vs0 ], {Gs1,St1} = explist(Gs0, St0), {B,St2} = block(Line, Ss, St1), Anno = line_file_anno(Line, St2), {#gfor_stmt{l=Anno,vars=Vs1,gens=Gs1,body=B},St2}. %% fdef_stmt(Line, Name, Pars, Stmts, State) -> {Fdef,State}. %% Transform this to an assign. fdef_stmt(Line, Fname, Ps, B, St0) -> {V,F,St1} = functiondef(Line, Fname, Ps, B, St0), Anno = line_file_anno(Line, St1), {#assign_stmt{l=Anno,vars=[V],exps=[F]},St1}. %% functiondef(FunctionDef, State) -> {CFunc,State}. %% functiondef(Line, Pars, Block, State) -> {CFunc,State}. %% functiondef(Line, Name, Pars, Block, State) -> {Var,CFunc,State}. %% Have to specially handle the case where the function is a %% "method". All this really means is that the function has an extra %% parameter 'self' prepended to the parameter list. functiondef({functiondef,L,Ps,B}, St) -> functiondef(L, Ps, B, St). functiondef(L, Ps, Stmts, St0) -> {Cp,Cb,St1} = function_block(Ps, Stmts, St0), Anno = line_file_anno(L, St1), {#fdef{l=Anno,pars=Cp,body=Cb},St1}. functiondef(L, Name0, Ps0, B, St0) -> %% Check if method and transform method to 'NAME' and add self to vars. case is_method(Name0) of %Export Name1 and Ps1 {yes,Name1} -> Ps1 = [{'NAME',L,<<"self">>}|Ps0]; no -> Name1 = Name0, Ps1 = Ps0 end, {Var,St1} = funcname(Name1, St0), {F0,St2} = functiondef(L, Ps1, B, St1), %% Add the function name to the annotations. Anno = luerl_anno:set(name, flat_funcname(Name1), F0#fdef.l), F1 = F0#fdef{l=Anno}, {Var,F1,St2}. is_method({'NAME',_,_}) -> no; is_method({'.',L,N,Rest0}) -> case is_method(Rest0) of {yes,Rest1} -> {yes,{'.',L,N,Rest1}}; no -> no %No change end; is_method({method,_,{'NAME',_,_}=N}) -> {yes,N}. flat_funcname(Name) -> list_to_binary(flat_funcname(Name, [])). flat_funcname({'NAME',_,N}, Rest) -> [N|Rest]; flat_funcname({'.',_,L,R}, Rest) -> flat_funcname(L, [<<".">>|flat_funcname(R, Rest)]). %% funcname(FuncNameExp, State) -> {CFuncNameExp,State}. funcname({'.',L,Exp,Rest}, St0) -> {Ce,St1} = funcname_first(Exp, St0), {Cr,St2} = funcname_rest(Rest, St1), {dot(L, Ce, Cr),St2}; funcname({'NAME',L,N}, St) -> {var_name(L, N),St}. funcname_first({'NAME',L,N}, St) -> {var_name(L, N),St}. funcname_rest({'.',L,Exp,Rest}, St0) -> {Ce,St1} = funcname_element(Exp, St0), {Cr,St2} = funcname_rest(Rest, St1), {dot(L, Ce, Cr),St2}; funcname_rest(Exp, St) -> funcname_last(Exp, St). funcname_element({'NAME',L,N}, St) -> %% Transform this to key_field with the name string. {#key{l=L,key=lit_name(L, N)},St}. %% Method call has been transformed away funcname_last({'NAME',L,N}, St) -> %% Transform this to key_field with the name string. {#key{l=L,key=lit_name(L, N)},St}. %% local_stmt(Line, Local, State) -> {Assign,State}. %% Create and assign local variables. %% local_stmt(Line, {functiondef,Lf,Name,Ps,B}, St0) -> %% {Var,F,St1} = functiondef(Lf, Name, Ps, B, St0), %% Anno = line_file_anno(Line, St1), %% {#local_fdef_stmt{l=Anno,var=Var,func=F},St1}; local_stmt(Line, {assign,_,Ns,Es}, St0) -> {Ces,St1} = explist(Es, St0), {Cns,St2} = lists:mapfoldl(fun (V, St) -> var(V, St) end, St1, Ns), Anno = line_file_anno(Line, St2), {#local_assign_stmt{l=Anno,vars=Cns,exps=Ces},St2}. %% expr_stmt(Line, Exp, State) -> {Call,State}. %% The expression pseudo statement. This will return a single value. expr_stmt(Line, Exp, St0) -> {Ce,St1} = exp(Exp, St0), Anno = line_file_anno(Line, St1), {#expr_stmt{l=Anno,exp=Ce},St1}. %% explist(Exprs, State) -> {Ins,State}. %% exp(Expression, State) -> {Ins,State}. explist([E|Es], St0) -> {Ce,St1} = exp(E, St0), {Ces,St2} = explist(Es, St1), {[Ce|Ces],St2}; explist([], St) -> {[],St}. %No expressions at all exp({nil,L}, St) -> {#lit{l=L,val=nil},St}; exp({false,L}, St) -> {#lit{l=L,val=false},St}; exp({true,L}, St) -> {#lit{l=L,val=true},St}; exp({'NUMERAL',L,N}, St) -> {#lit{l=L,val=N},St}; exp({'LITERALSTRING',L,S}, St) -> {#lit{l=L,val=S},St}; exp({'...',L}, St) -> {var_name(L, '...'),St}; %% {#lit{l=L,v='...'},St}; exp({functiondef,L,Ps,B}, St0) -> {Cf,St1} = functiondef(L, Ps, B, St0), {Cf,St1}; exp({table,L,Fs}, St0) -> {Cfs,St1} = tableconstructor(Fs, St0), {#tabcon{l=L,fields=Cfs},St1}; exp({op,L,Op,A1,A2}, St0) -> {Ca1,St1} = exp(A1, St0), {Ca2,St2} = exp(A2, St1), {#op{l=L,op=Op,args=[Ca1,Ca2]},St2}; exp({op,L,Op,A}, St0) -> {Ca,St1} = exp(A, St0), {#op{l=L,op=Op,args=[Ca]},St1}; exp(E, St) -> prefixexp(E, St). %% prefixexp(PrefixExp, State) -> {CPrefixExp,State}. prefixexp({'.',L,Exp,Rest}, St0) -> {Ce,St1} = prefixexp_first(Exp, St0), {Cr,St2} = prefixexp_rest(Rest, St1), {dot(L, Ce, Cr),St2}; prefixexp(P, St) -> prefixexp_first(P, St). prefixexp_first({'NAME',L,N}, St) -> {var_name(L, N),St}; prefixexp_first({single,L,E}, St0) -> {Ce,St1} = exp(E, St0), {#single{l=L,exp=Ce},St1}. prefixexp_rest({'.',L,Exp,Rest}, St0) -> {Ce,St1} = prefixexp_element(Exp, St0), {Cr,St2} = prefixexp_rest(Rest, St1), {dot(L, Ce, Cr),St2}; prefixexp_rest(Exp, St) -> prefixexp_element(Exp, St). prefixexp_element({'NAME',L,N}, St) -> %% Transform this to a key_field with the name string {#key{l=L,key=lit_name(L, N)},St}; prefixexp_element({key_field,L,Exp}, St0) -> {Ce,St1} = exp(Exp, St0), {#key{l=L,key=Ce},St1}; prefixexp_element({functioncall,L,Args}, St0) -> {Cas,St1} = explist(Args, St0), Anno = line_file_anno(L, St1), {#fcall{l=Anno,args=Cas},St1}; prefixexp_element({methodcall,Lm,{'NAME',Ln,N},Args}, St0) -> {Args1,St1} = explist(Args, St0), Anno = line_file_anno(Lm, St1), {#mcall{l=Anno,meth=lit_name(Ln, N),args=Args1},St1}. dot(L, Exp, Rest) -> #dot{l=L,exp=Exp,rest=Rest}. function_block(Pars, Stmts, St0)-> {Cps,St1} = make_local_pars(Pars, St0), {Cs,St2} = stmts(Stmts, St1), %% io:format("fb: ~p\n", [{St3#comp.fs}]), {Cps,Cs,St2}. make_local_pars(Ps, St) -> Add = fun ({'NAME',L,N}, S) -> {var_name(L, N),S}; ({'...',L}, S) -> {var_name(L, '...'),S} end, lists:mapfoldl(Add, St, Ps). %% tableconstrutor(Fields, State) -> {Instrs,State}. %% Build the instructions to construct a table. We could be smarter %% here and recognise already uses keys and only actually insert the %% last one. Or we could pre-order the table elements so the keys are %% already sorted. We can't unpack the last field if it is a multiple %% value efield as this must be done at run-time. tableconstructor(Fs, St0) -> %% N.B. this fun is for a MAPFOLDL!! Fun = fun ({exp_field,L,Ve}, S0) -> {Ce,S1} = exp(Ve, S0), %Value {#efield{l=L,val=Ce},S1}; ({name_field,L,{'NAME',Ln,N},Ve}, S0) -> {Ce,S1} = exp(Ve, S0), %Value {#kfield{l=L,key=lit_name(Ln, N),val=Ce},S1}; ({key_field,L,Ke,Ve}, S0) -> {Ck,S1} = exp(Ke, S0), %Key {Cv,S2} = exp(Ve, S1), %Value {#kfield{l=L,key=Ck,val=Cv},S2} end, {Cfs,St1} = lists:mapfoldl(Fun, St0, Fs), {Cfs,St1}. %% var_name(Line, Name) -> #var{}. %% lit_name(Line, Name) -> #lit{}. lit_name(L, N) -> #lit{l=L,val=N}. var_name(L, N) -> #var{l=L,name=N}. %% line_file_anno(Line, State) -> Anno. %% set_anno(KeyList, Anno) -> Anno. line_file_anno(L, St) -> Anno = luerl_anno:new(L), luerl_anno:set(file, St#cinfo.lfile, Anno). %% set_anno(Ps, Anno) -> %% lists:foldl(fun ({Key,Val}, A) -> luerl_anno:set(Key, Val, A) end, %% Anno, Ps). rvirding-luerl-772b25e/src/luerl_lib_string_format.erl0000664000232200023220000003212115114130670023565 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl_lib_string_format.erl %% Author : Robert Virding %% Purpose : The string formatting for Luerl. -module(luerl_lib_string_format). -include("luerl.hrl"). ?MODULEDOC(false). -export([format/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this %% Luerl definitions of these types. -define(WHITE_SPACE(C), (is_integer(C) andalso C >= $\000 andalso C =< $\s)). -define(UPPER(C), (C >= $A andalso C =< $Z)). -define(LOWER(C), (C >= $a andalso C =< $z)). -define(ASCII(C), (C >= 0 andalso C =< 127)). -define(DIGIT(C), (C >= $0 andalso C =< $9)). -define(HEX(C), (C >= $A andalso C =< $F orelse C >= $a andalso C =< $f orelse ?DIGIT(C))). format(F, As, St0) -> {Str,St1} = format_loop(luerl_lib:arg_to_list(F), As, St0), {[iolist_to_binary(Str)],St1}. format_loop(Fmt, As, St) -> format_loop(Fmt, As, St, []). format_loop([$%|Fmt0], As0, St0, Acc) -> {Format,Fmt1} = collect(Fmt0), {Out,As1,St1} = build(Format, As0, St0), format_loop(Fmt1, As1, St1, [Out|Acc]); format_loop([$\\,C|Fmt], As, St, Acc) -> format_loop(Fmt, As, St, [C|Acc]); format_loop([C|Fmt], As, St, Acc) -> format_loop(Fmt, As, St, [C|Acc]); format_loop([], _, St, Acc) -> %Ignore extra arguments {lists:reverse(Acc),St}. %% collect(Format) -> {{C,Flags,Field,Precision},Format}. %% Collect a conversion specification. %% %[flags][width][.precision][conversion] collect(Fmt0) -> %% io:format("~w\n", [Fmt0]), {Fl,Fmt1} = flags(Fmt0), %The flags characters {Fw,Fmt2} = field_width(Fmt1), %The field width {P,Fmt3} = precision(Fmt2), %The precision {C,Fmt4} = collect_cc(Fmt3), %The control character %% io:format("col C=~c Fl=~.2b Fw=~w P=~w\n", [C,Fl,Fw,P]), {{C,Fl,Fw,P},Fmt4}. %% Handling the flags of a format. %% Yes, we should use a tuple or record, but this is much more fun. -define(FL_NONE, 0). -define(FL_H, 2#00001). %# flag -define(FL_Z, 2#00010). %0 flag -define(FL_M, 2#00100). %- flag -define(FL_S, 2#01000). %space flag -define(FL_P, 2#10000). %+ flag -define(SET_FLAG(FL,B), (FL bor (B))). -define(FLAG_SET(FL, B), ((FL band (B)) =/= 0)). -define(FLAG_CLR(FL, B), ((FL band (B)) =:= 0)). flags(Fmt) -> flags(Fmt, ?FL_NONE). flags([$#|Fmt], Fl) -> flags(Fmt, ?SET_FLAG(Fl, ?FL_H)); flags([$0|Fmt], Fl) -> flags(Fmt, ?SET_FLAG(Fl, ?FL_Z)); flags([$-|Fmt], Fl) -> flags(Fmt, ?SET_FLAG(Fl, ?FL_M)); flags([$\s|Fmt], Fl) -> flags(Fmt, ?SET_FLAG(Fl, ?FL_S)); flags([$+|Fmt], Fl) -> flags(Fmt, ?SET_FLAG(Fl, ?FL_P)); flags(Fmt, Fl) -> {Fl,Fmt}. field_width(Fmt) -> %% io:format("fv ~s\n", [Fmt]), field_value(Fmt). precision([$.|Fmt]) -> field_value(Fmt); precision(Fmt) -> {none,Fmt}. collect_cc([C|Fmt]) -> {C,Fmt}; collect_cc([]) -> {none,[]}. field_value([C|_]=Fmt) when ?DIGIT(C) -> field_value(Fmt, 0); field_value(Fmt) -> {none,Fmt}. field_value([C|Fmt], F) when ?DIGIT(C) -> field_value(Fmt, 10*F + (C - $0)); field_value(Fmt, F) -> {F,Fmt}. %% build({Conversion,Flags,FieldWidth,Precision}, Args) -> {Out,Args}. %% Build a string from the conversion specification. %% Implemented conversions are d,i o,u,x,X e,E f,F g,G c s %. %% No length modifiers, h L l, no conversions n p S C allowed. %% Integer formats. build({$c,Fl,Fw,_P}, [A|As], St) -> N = luerl_lib:arg_to_integer(A), C = N band 255, {adjust_str([C], Fl, Fw),As,St}; build({Conv,Fl,Fw,P}, [A|As], St0) when Conv =:= $d ; Conv =:= $i -> {S,St1} = format_decimal(Fl, Fw, P, A, St0), {S,As,St1}; build({$u,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_unsigned(Fl, Fw, P, A, St0), {S,As,St1}; build({$o,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_octal(Fl, Fw, P, A, St0), {S,As,St1}; build({$x,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_hex(Fl, Fw, P, A, St0), {S,As,St1}; build({$X,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_HEX(Fl, Fw, P, A, St0), {S,As,St1}; %% Float formats. build({$e,Fl,Fw,P}, [A|As], St) -> {format_e_float(Fl, Fw, P, A),As,St}; build({$E,Fl,Fw,P}, [A|As], St) -> {format_e_float(Fl, Fw, P, A),As,St}; build({$f,Fl,Fw,P}, [A|As], St) -> {format_f_float(Fl, Fw, P, A),As,St}; build({$F,Fl,Fw,P}, [A|As], St) -> {format_f_float(Fl, Fw, P, A),As,St}; build({$g,Fl,Fw,P}, [A|As], St) -> {format_g_float(Fl, Fw, P, A),As,St}; build({$G,Fl,Fw,P}, [A|As], St) -> {format_g_float(Fl, Fw, P, A),As,St}; %% %p build({$q,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_q(Fl, Fw, P, A, St0), {S,As,St1}; build({$s,Fl,Fw,P}, [A|As], St0) -> {S,St1} = format_s(Fl, Fw, P, A, St0), {S,As,St1}; % Literal % format. build({$%,?FL_NONE,none,none}, As, St) -> %No flags, field or precision! {"%",As,St}. %% format_decimal(Flags, Field, Precision, Argument, State) -> {String,State}. %% format_octal(Flags, Field, Precision, Argument, State) -> {String,State}. %% format_hex(Flags, Field, Precision, Argument, State) -> {String,State}. %% format_HEX(Flags, Field, Precision, Argument, State) -> {String,State}. %% format_unsigned(Flags, Field, Precision, Argument, State) -> {String,State}. %% Print integer Number with base Base. This is a bit messy as we are %% following string.format handling. Could actually generate any %% error here as the error will be caught in the string.format %% call. But to be safe. format_decimal(Fl, _F, _P, A, St) when ?FLAG_SET(Fl, ?FL_H) -> badarg_error(format, ['d',A], St); %Could just generate any error format_decimal(Fl, F, P, A, St) -> I = luerl_lib:arg_to_integer(A), Str = integer_to_list(abs(I), 10), {format_integer(Fl, F, P, I, "", Str),St}. format_octal(Fl, _F, _P, A, St) when ?FLAG_SET(Fl, ?FL_S) -> badarg_error(format, ['o',A], St); %Could just generate any error format_octal(Fl, F, P, A, St) -> I = luerl_lib:arg_to_integer(A), Str = integer_to_list(abs(I), 8), {format_integer(Fl, F, P, I, "0", Str),St}. format_hex(Fl, F, P, A, St) -> I = luerl_lib:arg_to_integer(A), Str = lists:flatten(io_lib:fwrite("~.16b", [abs(I)])), {format_integer(Fl, F, P, I, "0x", Str),St}. format_HEX(Fl, F, P, A, St) -> I = luerl_lib:arg_to_integer(A), Str = lists:flatten(io_lib:fwrite("~.16B", [abs(I)])), {format_integer(Fl, F, P, I, "0X", Str),St}. format_unsigned(Fl, _F, _P, A, St) when ?FLAG_SET(Fl, ?FL_H bor ?FL_S bor ?FL_P) -> badarg_error(format, ['u',A], St); format_unsigned(Fl, F, P, A, St) -> %% We have to make the number always positive so we are assuming %% 124 bit integers which matches Lua 5.3. N = luerl_lib:arg_to_integer(A), I = if N >= 0 -> N; true -> N band 16#FFFFFFFFFFFFFFFF end, Str = integer_to_list(I), {format_integer(Fl, F, P, I, Str),St}. %% format_integer(Flag, Field, Precision, Number, String) -> String. %% format_integer(Flag, Field, Precision, Number, Prefix, String) -> String. %% Do the actual formatting of integers after the checking. format_integer(Fl, F, P, N, Str) -> format_integer(Fl, F, P, N, "", Str). format_integer(Fl, F, P, N, Prefix, Str0) -> %% The # says add the prefix. Str1 = if ?FLAG_SET(Fl, ?FL_H) -> Prefix ++ Str0; true -> Str0 end, Sign = sign(Fl, N), if P =/= none -> Str2 = Sign ++ lists:flatten(adjust_str(Str1, ?FL_Z, P)), adjust_str(Str2, (Fl band ?FL_M), F); ?FLAG_SET(Fl, ?FL_M) -> Str2 = Sign ++ Str1, adjust_str(Str2, Fl, F); ?FLAG_SET(Fl, ?FL_Z) andalso F =/= none -> Str2 = adjust_str(Str1, ?FL_Z, F-length(Sign)), Sign ++ Str2; true -> Str2 = Sign ++ Str1, adjust_str(Str2, Fl, F) end. %% format_e_float(Flags, Field, Precision, Argument) -> String. %% format_f_float(Flags, Field, Precision, Argument) -> String. %% format_g_float(Flags, Field, Precision, Argument) -> String. %% Print float Argument in e/f/g format. format_e_float(Fl, F, P, A) -> format_float(Fl, F, e_float_precision(P), "~.*e", A). format_f_float(Fl, F, P, A) -> format_float(Fl, F, f_float_precision(P), "~.*f", A). format_g_float(Fl, F, P, A) -> format_float(Fl, F, g_float_precision(P), "~.*g", A). %% format_float(Flag, Field, Precision, Format, Argument) -> String format_float(Fl, F, P, Format, A) -> N = luerl_lib:arg_to_float(A), Str0 = lists:flatten(io_lib:format(Format, [P,abs(N)])), Sign = sign(Fl, N), if ?FLAG_SET(Fl, ?FL_M) -> Str1 = Sign ++ Str0, adjust_str(Str1, Fl, F); ?FLAG_SET(Fl, ?FL_Z) andalso (F =/= none) -> Str1 = adjust_str(Str0, ?FL_Z, F-length(Sign)), Sign ++ Str1; true -> Str1 = Sign ++ Str0, adjust_str(Str1, Fl, F) end. e_float_precision(none) -> 7; e_float_precision(P) -> P+1. f_float_precision(none) -> 6; f_float_precision(P) -> P. g_float_precision(none) -> 6; g_float_precision(P) -> P. %% sign(Flags, Number) -> SignString. sign(_, N) when N < 0 -> "-"; %Always sign when N<0 sign(Fl, _) -> if ?FLAG_SET(Fl, ?FL_P) -> "+"; %+ flag has priority ?FLAG_SET(Fl, ?FL_S) -> " "; true -> "" end. %% format_s(Flags, Field, Precision, Argument, State) -> {String,State}. format_s(Fl, Fw, P, A, St0) -> {S0,St1} = luerl_lib:tostring(A, St0), %% If any field and there is a 0 in the string then we have an error. %% Lua wants this. Fargs = (Fl =/= ?FL_NONE) orelse (Fw =/= none) orelse (P =/= none), case Fargs andalso binary:match(S0, <<0>>) =/= nomatch of true -> badarg_error(format, ['s',A], St1); false -> false end, S1 = trim_bin(S0, P), S2 = adjust_bin(S1, Fl, Fw), {S2,St1}. trim_bin(Bin, Prec) when is_integer(Prec), byte_size(Bin) > Prec -> binary:part(Bin, 0, Prec); trim_bin(Bin, _) -> Bin. %% adjust_bin(Binary, Flags, Field) -> iolist(). %% adjust_str(String, Flags, Field) -> iolist(). adjust_bin(Bin, ?FL_NONE, none) -> Bin; adjust_bin(Bin, Fl, F) when is_integer(F), byte_size(Bin) < F -> Size = byte_size(Bin), Padding = lists:duplicate(F-Size, pad_char(Fl, F)), if ?FLAG_SET(Fl, ?FL_M) -> [Bin,Padding]; true -> [Padding,Bin] end; adjust_bin(Bin, _, _) -> Bin. adjust_str(Str, ?FL_NONE, none) -> Str; adjust_str(Str, Fl, F) when is_integer(F), length(Str) < F -> Size = length(Str), Padding = lists:duplicate(F-Size, pad_char(Fl, F)), if ?FLAG_SET(Fl, ?FL_M) -> [Str,Padding]; true -> [Padding,Str] end; adjust_str(Str, _, _) -> Str. %% pad_char(Flags, Field) -> Char. pad_char(Fl, F) -> if ?FLAG_SET(Fl, ?FL_M) -> $\s; %'-' forces padding to " " ?FLAG_SET(Fl, ?FL_Z), F =/= none -> $0; true -> $\s end. %% format_q(Fl, Fw, P, Arg, State) -> {Striing,State}. %% Could actually generate any error here as the error will be caught %% in the string.format call. But to be safe. format_q(?FL_NONE, none, none, Arg, St) -> S = format_q(Arg, St), {S,St}; format_q(_Fl, _Fw, _P, Arg, St) -> badarg_error(format, ['q',Arg], St). %Could just generate any error %% format_q(Arg, State) -> String. %% Build the quote for the right argument types. format_q(S0, _St) when is_binary(S0) -> S1 = format_q_string(S0), [$",S1,$"]; format_q(I, _St) when is_integer(I) -> integer_to_binary(I); format_q(I, _St) when is_float(I) -> float_to_binary(I); format_q(nil, _St) -> <<"nil">>; format_q(true, _St) -> <<"true">>; format_q(false, _St) -> <<"false">>; format_q(Arg, St) -> badarg_error(format, ['q',Arg], St). %% format_q_string(String) -> String. %% Build the quoted string. format_q_string(<<$\\,Q/binary>>) -> [$\\,$\\|format_q_string(Q)]; format_q_string(<<$\",Q/binary>>) -> [$\\,$\"|format_q_string(Q)]; format_q_string(<<$\n,Q/binary>>) -> [$\\,$\n|format_q_string(Q)]; %% Control characters. format_q_string(<>) when C1 >= 0, C1 =< 31 -> format_q_dec(C1, Q); format_q_string(<>) when C1 >= 127, C1 =< 159 -> format_q_dec(C1, Q); format_q_string(<<173,Q/binary>>) -> %% Don't ask me why we do this for 173. [io_lib:format("\\173",[])|format_q_string(Q)]; %% And the rest. format_q_string(<>) -> [C|format_q_string(Q)]; format_q_string(<<>>) -> []. format_q_dec(C1, <<>> = Q) -> [io_lib:format("\\~w", [C1])|format_q_string(Q)]; format_q_dec(C1, <> = Q) when not ?DIGIT(C2) -> [io_lib:format("\\~w", [C1])|format_q_string(Q)]; format_q_dec(C1, Q) -> [io_lib:format("\\~.3.0w",[C1])|format_q_string(Q)]. rvirding-luerl-772b25e/src/luerl_comp_vars.erl0000664000232200023220000003222315114130670022055 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : luerl_comp_vars.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Does variable and stack analysis in the compiler -module(luerl_comp_vars). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). -export([chunk/2]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,subtract/2,intersection/2]). %% chunk(Code, CompInfo) -> {ok,Code}. chunk(Code0, #cinfo{opts=Opts}=_Ci) -> %% No local state here! {Code1,_,_,nul} = functiondef(Code0, [], nul), luerl_comp:debug_print(Opts, "cv: ~p\n", [Code1]), {ok,Code1}. %% stmts(Stmts, VarData, State) -> %% {Stmts,NewVarData,State}. %% Main problem here is to calculate local/free/used variables in the %% right order. Must do everything going forwards. stmts([S0|Ss0], Vars0, St0) -> {S1,New,Used,Fused,St1} = stmt(S0, nul, St0), Vars1 = update_vars(Vars0, New, Used, Fused), %% io:format("ss1: ~p\n", [Vars0]), %% io:format("ss1> ~p\n", [{New,Used,Fused}]), %% io:format("ss1> ~p\n", [Vars1]), {Ss1,Vars2,St2} = stmts(Ss0, Vars1, St1), {[S1|Ss1],Vars2,St2}; stmts([], Vars, St) -> {[],Vars,St}. update_vars(#vars{local=Lo,free=Fr,used=Us,fused=Fu}, New, Used, Fused) -> Aused = union(Used, Fused), %All used Free = subtract(Aused, Lo), #vars{local=union(New, Lo), free=union(Free, Fr), used=union(Used, Us), fused=union(Fused, Fu)}. %% stmt(Stmt, LocalVars, State) -> {Stmt,NewVars,UsedVars,FusedVars,State}. stmt(#assign_stmt{}=A, Loc, St) -> assign_stmt(A, Loc, St); stmt(#call_stmt{}=C, Loc, St) -> call_stmt(C, Loc, St); stmt(#return_stmt{}=R, Loc, St) -> return_stmt(R, Loc, St); stmt(#break_stmt{}=B, _, St) -> {B,[],[],[],St}; stmt(#block_stmt{}=B, Loc, St) -> block_stmt(B, Loc, St); stmt(#while_stmt{}=W, Loc, St) -> while_stmt(W, Loc, St); stmt(#repeat_stmt{}=R, Loc, St) -> repeat_stmt(R, Loc, St); stmt(#if_stmt{}=If, Loc, St) -> if_stmt(If, Loc, St); stmt(#nfor_stmt{}=For, Loc, St) -> numfor_stmt(For, Loc, St); stmt(#gfor_stmt{}=For, Loc, St) -> genfor_stmt(For, Loc, St); stmt(#local_assign_stmt{}=L, Loc, St) -> local_assign_stmt(L, Loc, St); stmt(#local_fdef_stmt{}=L, Loc, St) -> local_fdef_stmt(L, Loc, St); stmt(#expr_stmt{}=E, Loc, St) -> %Expressions "statement" expr_stmt(E, Loc, St). %% assign_stmt(Assign, LocalVars, State) -> %% {Assign,NewVars,UsedVars,FusedVars,State}. assign_stmt(#assign_stmt{vars=Vs0,exps=Es0}=A, Loc, St0) -> {Vs1,Vused,Vfused,St1} = assign_loop(Vs0, Loc, St0), {Es1,Eused,Efused,St2} = explist(Es0, Loc, St1), Used = union(Vused, Eused), Fused = union(Vfused, Efused), {A#assign_stmt{vars=Vs1,exps=Es1},[],Used,Fused,St2}. assign_loop([V0|Vs0], Loc, St0) -> {V1,Vused,Vfused,St1} = var(V0, Loc, St0), {Vs1,Vsused,Vsfused,St2} = assign_loop(Vs0, Loc, St1), Used = union(Vused, Vsused), Fused = union(Vfused, Vsfused), {[V1|Vs1],Used,Fused,St2}; assign_loop([], _, St) -> {[],[],[],St}. var(#dot{exp=Exp0,rest=Rest0}=D, Loc, St0) -> {Exp1,Eused,Efused,St1} = prefixexp_first(Exp0, Loc, St0), {Rest1,Rused,Rfused,St2} = var_rest(Rest0, Loc, St1), Used = union(Eused, Rused), Fused = union(Efused, Rfused), {D#dot{exp=Exp1,rest=Rest1},Used,Fused,St2}; var(#var{name=N}=V, _, St) -> {V,[N],[],St}. var_rest(#dot{exp=Exp0,rest=Rest0}=D, Loc, St0) -> {Exp1,Eused,Efused,St1} = prefixexp_element(Exp0, Loc, St0), {Rest1,Rused,Rfused,St2} = var_rest(Rest0, Loc, St1), Used = union(Eused, Rused), Fused = union(Efused, Rfused), {D#dot{exp=Exp1,rest=Rest1},Used,Fused,St2}; var_rest(Exp, Loc, St) -> var_last(Exp, Loc, St). var_last(#key{key=Exp0}=K, Loc, St0) -> {Exp1,Used,Fused,St1} = exp(Exp0, Loc, St0), {K#key{key=Exp1},Used,Fused,St1}. %% call_stmt(Call, LocalVars, State) -> %% {Call,NewVars,UsedVars,FusedVars,State}. call_stmt(#call_stmt{call=Exp0}=C, Loc, St0) -> {Exp1,Used,Fused,St1} = exp(Exp0, Loc, St0), {C#call_stmt{call=Exp1},[],Used,Fused,St1}. %% return_stmt(Return, LocalVars, State) -> %% {Return,NewVars,UsedVars,FusedVars,State}. return_stmt(#return_stmt{exps=Es0}=R, Loc, St0) -> {Es1,Used,Fused,St1} = explist(Es0, Loc, St0), {R#return_stmt{exps=Es1},[],Used,Fused,St1}. %% block_stmt(Block, LocalVars, State) -> %% {Block,NewVars,UsedVars,FusedVars,State}. block_stmt(#block_stmt{body=Ss0}=B, _, St0) -> Vars0 = #vars{local=[],free=[],used=[],fused=[]}, {Ss1,Vars1,St1} = stmts(Ss0, Vars0, St0), %% Be careful what we export, adjust Used and Fused for locals. #vars{local=Bloc,used=Bused,fused=Bfused} = Vars1, Used = subtract(Bused, Bloc), Fused = subtract(Bfused, Bloc), {B#block_stmt{body=Ss1,vars=Vars1},[],Used,Fused,St1}. %% do_block(Block, State) -> {Block,UsedVars,FusedVars,State}. %% do_block(Block, LocalVars, State) -> {Block,UsedVars,FusedVars,State}. %% Do_block never returns external new variables as it never exports %% variables. do_block(B, St) -> do_block(B, [], St). do_block(#block{body=Ss0}=B, Loc, St0) -> Vars0 = #vars{local=Loc,free=[],used=[],fused=[]}, {Ss1,Vars1,St1} = stmts(Ss0, Vars0, St0), %% Be careful what we export, adjust Used and Fused for locals. #vars{local=Bloc,used=Bused,fused=Bfused} = Vars1, Used = subtract(Bused, Bloc), Fused = subtract(Bfused, Bloc), {B#block{body=Ss1,vars=Vars1},Used,Fused,St1}. %% while_stmt(While, LocalVars, State) -> %% {While,NewVars,UsedVars,FusedVars,State}. %% While_stmt never returns external new variables. The test %% expression is done in the context of the surrounding block. while_stmt(#while_stmt{exp=E0,body=B0}=W, Loc, St0) -> {E1,Eused,Efused,St1} = exp(E0, Loc, St0), {B1,Bused,Bfused,St2} = do_block(B0, St1), Used = union(Eused, Bused), Fused = union(Efused, Bfused), {W#while_stmt{exp=E1,body=B1},[],Used,Fused,St2}. %% repeat_stmt(Repeat, LocalVars, State) -> %% {Repeat,NewVars,UsedVars,FusedVars,State}. %% Repeat_stmt never returns external new variables. The test %% expression is done in the context of the repeat block and is %% already inside the block. repeat_stmt(#repeat_stmt{body=B0}=R, _, St0) -> {B1,Used,Fused,St1} = do_block(B0, St0), {R#repeat_stmt{body=B1},[],Used,Fused,St1}. %% if_stmt(If, LocalVars, State) -> {If,NewVars,FreeVars,State}. %% The block info includes anything from the test expressions even %% though we keep them separate. if_stmt(#if_stmt{tests=Ts0,else_block=E0}=If, Loc, St0) -> {Ts1,Tused,Tfused,St1} = if_tests(Ts0, Loc, St0), {E1,Eused,Efused,St2} = do_block(E0, St1), Used = union(Tused, Eused), Fused = union(Tfused, Efused), {If#if_stmt{tests=Ts1,else_block=E1},[],Used,Fused,St2}. if_tests([{E0,B0}|Ts0], Loc, St0) -> {E1,Eused,Efused,St1} = exp(E0, Loc, St0), {B1,Bused,Bfused,St2} = do_block(B0, St1), {Ts1,Tsused,Tsfused,St3} = if_tests(Ts0, Loc, St2), Used = union([Eused,Bused,Tsused]), Fused = union([Efused,Bfused,Tsfused]), {[{E1,B1}|Ts1],Used,Fused,St3}; if_tests([], _, St) -> {[],[],[],St}. %% numfor_stmt(For, LocalVars, State) -> %% {For,NewVars,UsedVars,FusedVars,State}. numfor_stmt(#nfor_stmt{var=#var{name=N},init=I0,limit=L0,step=S0,body=B0}=For, Loc, St0) -> {[I1,L1,S1],Esused,Esfused,St1} = explist([I0,L0,S0], Loc, St0), {B1,Bused,Bfused,St2} = do_block(B0, [N], St1), %% Be careful what we export, adjust Used and Fused for N. Used = union(Esused, del_element(N, Bused)), Fused = union(Esfused, del_element(N, Bfused)), {For#nfor_stmt{init=I1,limit=L1,step=S1,body=B1},[],Used,Fused,St2}. %% genfor_stmt(For, LocalVars, State) -> {For,NewVars,FreeVars,State}. genfor_stmt(#gfor_stmt{vars=Vs,gens=Gs0,body=B0}=For, Loc, St0) -> {Gs1,Gused,Gfused,St1} = explist(Gs0, Loc, St0), Ns = lists:foldl(fun (#var{name=N}, Ns) -> add_element(N, Ns) end, [], Vs), {B1,Bused,Bfused,St2} = do_block(B0, Ns, St1), %% Be careful what we export, adjust Used and Fused for Ns. Used = union(Gused, subtract(Bused, Ns)), Fused = union(Gfused, subtract(Bfused, Ns)), {For#gfor_stmt{gens=Gs1,body=B1},[],Used,Fused,St2}. %% local_assign_stmt(Local, LocalVars, State) -> {Local,NewVars,FreeVars,State}. local_assign_stmt(#local_assign_stmt{vars=Vs,exps=Es0}=L, Loc, St0) -> {Es1,Used,Fused,St1} = explist(Es0, Loc, St0), New = lists:foldl(fun (#var{name=N}, Ns) -> add_element(N, Ns) end, [], Vs), {L#local_assign_stmt{exps=Es1},New,Used,Fused,St1}. %% local_fdef_stmt(Local, LocalVars, State) -> %% {Local,NewVars,FreeVars,UsedVars,State}. %% We explicitly handle used variables here as we want the function %% name to be included in Used in recursive function calls. local_fdef_stmt(#local_fdef_stmt{var=#var{name=N},func=F0}=L, _, St0) -> {F1,Used,Fused,St1} = functiondef(F0, nul, St0), New = [N], {L#local_fdef_stmt{func=F1},New,Used,Fused,St1}. %% exp_stmt(Expr, LocalVars, State) -> %% {Expr,NewVars,UsedVars,FusedVars,State}. %% This will return a single value. expr_stmt(#expr_stmt{exp=Exp0}=E, Loc, St0) -> {Exp1,Used,Fused,St1} = exp(Exp0, Loc, St0), {E#expr_stmt{exp=Exp1},[],Used,Fused,St1}. %% explist(Exprs, LocalVars, State) -> {Exprs,UsedVars,FusedVars,State}. %% exp(Expr, LocalVars, State) -> {Expr,UsedVars,FusedVars,State}. %% prefixexp(Expr, LocalVars, State) -> {Expr,UsedVars,FusedVars,State}. %% An expression can never create new local variables. explist([E0|Es0], Loc, St0) -> {E1,Eused,Efused,St1} = exp(E0, Loc, St0), {Es1,Esused,Esfused,St2} = explist(Es0, Loc, St1), Used = union(Eused, Esused), Fused = union(Efused, Esfused), {[E1|Es1],Used,Fused,St2}; explist([], _, St) -> {[],[],[],St}. %No expressions at all exp(#lit{}=L, _, St) -> {L,[],[],St}; %Nothing to do exp(#fdef{}=F, _, St) -> functiondef(F, nul, St); exp(#op{args=Es0}=Op, Loc, St0) -> {Es1,Used,Fused,St1} = explist(Es0, Loc, St0), {Op#op{args=Es1},Used,Fused,St1}; exp(#tabcon{fields=Fs0}=T, Loc, St0) -> {Fs1,Used,Fused,St1} = tableconstructor(Fs0, Loc, St0), {T#tabcon{fields=Fs1},Used,Fused,St1}; exp(E, Loc, St) -> prefixexp(E, Loc, St). prefixexp(#dot{exp=Exp0,rest=Rest0}=D, Loc, St0) -> {Exp1,Eused,Efused,St1} = prefixexp_first(Exp0, Loc, St0), {Rest1,Rused,Rfused,St2} = prefixexp_rest(Rest0, Loc, St1), Used = union(Eused, Rused), Fused = union(Efused, Rfused), {D#dot{exp=Exp1,rest=Rest1},Used,Fused,St2}; prefixexp(Exp, Loc, St) -> prefixexp_first(Exp, Loc, St). prefixexp_first(#single{exp=E0}=S, Loc, St0) -> {E1,Used,Fused,St1} = exp(E0, Loc, St0), {S#single{exp=E1},Used,Fused,St1}; prefixexp_first(#var{name=N}=V, _, St) -> {V,[N],[],St}. prefixexp_rest(#dot{exp=Exp0,rest=Rest0}=D, Loc, St0) -> {Exp1,Eused,Efused,St1} = prefixexp_element(Exp0, Loc, St0), {Rest1,Rused,Rfused,St2} = prefixexp_rest(Rest0, Loc, St1), Used = union(Eused, Rused), Fused = union(Efused, Rfused), {D#dot{exp=Exp1,rest=Rest1},Used,Fused,St2}; prefixexp_rest(Exp, Loc, St) -> prefixexp_element(Exp, Loc, St). prefixexp_element(#key{key=E0}=K, Loc, St0) -> {E1,Used,Fused,St1} = exp(E0, Loc, St0), {K#key{key=E1},Used,Fused,St1}; prefixexp_element(#fcall{args=As0}=F, Loc, St0) -> {As1,Used,Fused,St1} = explist(As0, Loc, St0), {F#fcall{args=As1},Used,Fused,St1}; prefixexp_element(#mcall{meth=#lit{val=N},args=As0}=M, Loc, St0) -> {As1,Used,Fused,St1} = explist(As0, Loc, St0), {M#mcall{args=As1},add_element(N, Used),Fused,St1}. %% functiondef(Func, LocalVars, State) -> {Func,UsedVars,FusedVars,State}. %% All the variables "used" in the function which are not local %% become "fused" externally. functiondef(#fdef{pars=Ps,body=Ss0}=F, _, St0) -> Loc0 = lists:foldl(fun (#var{name=N}, Vs) -> add_element(N, Vs); (_, Vs) -> Vs end, [], Ps), Vars0 = #vars{local=Loc0,free=[],used=[],fused=[]}, {Ss1,Vars1,St1} = stmts(Ss0, Vars0, St0), %% Make all free variables "fused" in outside block. {F#fdef{body=Ss1,vars=Vars1},[],Vars1#vars.free,St1}. %% tableconstructor(Fields, LocalVars, State) -> %% {Fields,UsedVars,FusedVars,State}. tableconstructor(Fs0, Loc, St0) -> Fun = fun (#efield{val=V0}=F, {Used0,Fused0,S0}) -> {V1,Vused,Vfused,S1} = exp(V0, Loc, S0), Used1 = union(Vused, Used0), Fused1 = union(Vfused, Fused0), {F#efield{val=V1},{Used1,Fused1,S1}}; (#kfield{key=K0,val=V0}=F, {Used0,Fused0,S0}) -> {K1,Kused,Kfused,S1} = exp(K0, Loc, S0), {V1,Vused,Vfused,S2} = exp(V0, Loc, S1), Used1 = union([Kused,Vused,Used0]), Fused1 = union([Kfused,Vfused,Fused0]), {F#kfield{key=K1,val=V1},{Used1,Fused1,S2}} end, {Fs1,{Used,Fused,St1}} = lists:mapfoldl(Fun, {[],[],St0}, Fs0), {Fs1,Used,Fused,St1}. rvirding-luerl-772b25e/src/luerl.app.src0000664000232200023220000000226415114130670020572 0ustar debalancedebalance%% -*- mode: erlang; indent-tabs-mode: nil -*- %% Copyright (c) 2013-2024 Robert Virding %% %% 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. {application, luerl, [{description, "Luerl - an implementation of Lua on Erlang"}, {vsn, "1.5.0"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}, {env, []}, {mod, {luerl_app, []}}, %% Project metadata {licenses, ["Apache-2.0"]}, {links, [{"Github", "https://github.com/rvirding/luerl"}]}, %% This is used for hex packages. {files, ["README.md", "LICENSE", "VERSION", "src", "doc", "ebin/luerl.app", "include", "rebar.*", "*akefile", "*.escript"]}, {exclude_files, ["priv/images/*"]} ]}. rvirding-luerl-772b25e/src/luerl_scan.xrl0000664000232200023220000004327415114130670021043 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% -*- mode: erlang; indent-tabs-mode: nil -*- %% %% 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. %% File : luerl_scan.xrl %% Author : Robert Virding %% Purpose : Token definitions for LUA. Definitions. D = [0-9] H = [0-9A-Fa-f] U = [A-Z] L = [a-z] NAME = ({U}|{L}|_|{D}) SNAME = ({U}|{L}|_) Rules. %% Names/identifiers. ({U}|{L}|_)({U}|{L}|_|{D})* : name_token(TokenChars, TokenLine). %% Numbers, we parse integers and floats in one go as they can %% interact with each other. %% Hexadecimal numbers, we have separate rule to ensure we don't have %% just a '.'. NOTE THESE MUST COME FIRST TO CATCH 0[xX]!!!! %% %% 0[xX]{H}+\.?{H}+([pP][-+]?{D}+)?{NAME}* : %% 0[xX]{H}+\.?{H}*([pP][-+]?{D}+)?{NAME}* : %% %% 0[xX]{H}*\.{H}*([pP][-+]?{D}+)?{NAME}* : 0[xX]{H}*\.?{H}*([pP][-+]?{D}*)?{NAME}* : %% io:format("h2 ~p\n", [TokenChars]), hex_number_token(TokenChars, TokenLine). %% Decimal numbers, we separate rules to ensure we don't have just a '.'. %% Both integers and floats are handled here. %% %% {D}*\.?{D}*([eE][-+]?{D}+)?{NAME}* %% (({D}+\.?{D*})|(\.{D}+))([eE][-+]?{D}+)?{NAME}* : \.{D}+([eE][-+]?{D}+)?{NAME}* : %% io:format("d1 ~p\n", [TokenChars]), decimal_number_token(TokenChars, TokenLine). {D}+\.?{D}*([eE][-+]?{D}+)?{NAME}* : %% io:format("d2 ~p\n", [TokenChars]), decimal_number_token(TokenChars, TokenLine). %% Strings. %% Handle the illegal newlines in string_token. \"(\\.|\\\n|[^"\\])*\" : string_token(TokenChars, TokenLen, TokenLine). \'(\\.|\\\n|[^'\\])*\' : string_token(TokenChars, TokenLen, TokenLine). %% Handle multi line strings, [[ ]], [=[ ]=], [==[ ]==] %% This gets a bit tedious as we have to each case separately. \[\[([^]]|\][^]])*\]\] : long_string_token(TokenChars, TokenLen, 2, TokenLine). \[=\[([^]]|\](=[^]]|[^=]))*\]=\] : long_string_token(TokenChars, TokenLen, 3, TokenLine). \[==\[([^]]|\](==[^]]|=[^=]|[^=]))*\]==\] : long_string_token(TokenChars, TokenLen, 4, TokenLine). \[===\[([^]]|\](===[^]]|==[^=]|=[^=]|[^=]))*\]===\] : long_string_token(TokenChars, TokenLen, 5, TokenLine). %% \[==\[([^]]|\]==[^]]|\]=[^=]|\][^=])*\]==\] : %% Other known tokens. \+ : {token,{'+',TokenLine}}. \- : {token,{'-',TokenLine}}. \* : {token,{'*',TokenLine}}. \/ : {token,{'/',TokenLine}}. \// : {token,{'//',TokenLine}}. \% : {token,{'%',TokenLine}}. \^ : {token,{'^',TokenLine}}. \& : {token,{'&',TokenLine}}. \| : {token,{'|',TokenLine}}. \~ : {token,{'~',TokenLine}}. \>> : {token,{'>>',TokenLine}}. \<< : {token,{'<<',TokenLine}}. \# : {token,{'#',TokenLine}}. == : {token,{'==',TokenLine}}. ~= : {token,{'~=',TokenLine}}. <= : {token,{'<=',TokenLine}}. >= : {token,{'>=',TokenLine}}. < : {token,{'<',TokenLine}}. > : {token,{'>',TokenLine}}. = : {token,{'=',TokenLine}}. \( : {token,{'(',TokenLine}}. \) : {token,{')',TokenLine}}. \{ : {token,{'{',TokenLine}}. \} : {token,{'}',TokenLine}}. \[ : {token,{'[',TokenLine}}. \] : {token,{']',TokenLine}}. :: : {token,{'::',TokenLine}}. ; : {token,{';',TokenLine}}. : : {token,{':',TokenLine}}. , : {token,{',',TokenLine}}. \. : {token,{'.',TokenLine}}. \.\. : {token,{'..',TokenLine}}. \.\.\. : {token,{'...',TokenLine}}. [\011-\015\s\240]+ : skip_token. %Mirror Lua here %% Comments, either -- or --[[ ]]. %%--(\[([^[\n].*|\[\n|[^[\n].*|\n) : skip_token. --\n : skip_token. --[^[\n].* : skip_token. --\[\n : skip_token. --\[[^[\n].* : skip_token. %% Comment --ab ... yz --ab([^y]|y[^z])*yz --\[\[([^]]|\][^]])*\]\] : skip_token. --\[\[([^]]|\][^]])* : {error,"unfinished long comment"}. %% Catch other illegal tokens. . : illegal_token(TokenChars, TokenLine). Erlang code. -include("luerl.hrl"). ?MODULEDOC(false). -export([is_keyword/1]). -export([string_chars/1,chars/1]). %% Luerl definitions of these types. -define(WHITE_SPACE(C), (C >= $\000 andalso C =< $\s)). -define(ASCII(C), (C >= 0 andalso C =< 127)). -define(DIGIT(C), (C >= $0 andalso C =< $9)). -define(HEX(C), (C >= $A andalso C =< $F orelse C >= $a andalso C =< $f orelse ?DIGIT(C))). -define(CHAR(C), (C >= O andalso C < 16#110000)). -define(UNICODE(C), (is_integer(C) andalso (C >= 0 andalso C < 16#D800 orelse C > 16#DFFF andalso C < 16#FFFE orelse C > 16#FFFF andalso C =< 16#10FFFF))). -define(UNI255(C), (is_integer(C) andalso 0 =< C andalso C =< 16#ff)). %% illegal_token(Chars, Line) -> {error,E}. %% Generate a more Lua compatible error message. illegal_token(Chars, _Line) -> {error,"syntax error near '" ++ Chars ++ "'"}. %% name_token(Chars, Line) -> %% {token,{'NAME',Line,Symbol}} | {Name,Line} | {error,E}. %% Build a name from list of legal characters, else error. name_token(Cs, L) -> case catch {ok,list_to_binary(Cs)} of {ok,Name} -> case is_keyword(Name) of true -> {token,{name_string(Name),L}}; false -> {token,{'NAME',L,Name}} end; _ -> {error,"illegal name"} end. name_string(Name) -> binary_to_atom(Name, latin1). %Only latin1 in Lua %% decimal_number_token(TokenChars, TokenLine) %% {token,{'NUMERAL',TokenLine,Float}} | {error,E}. %% Build either an integer or a float from a decimal number %% string. We first collect the specific number section, then we %% create the number. This makes it easier to keep track of which %% sections we need to make Lua compliant Luerl numbers, as Lua has %% some very "specific" handling. %% %% \.{D}+([eE][-+]?{D}+)?{NAME}* : %% {D}+\.?{D}*([eE][-+]?{D}+)?{NAME}* : decimal_number_token(TokenChars, TokenLine) -> %% io:format("dnt ~p\n", [dec_number_split(TokenChars)]), Result = case dec_number_split(TokenChars) of %% If there is anything after the number sections then %% it is an error! {_,_,_,Rest} when Rest =/= [] -> error; {[],[],[],_Rest} -> error; %Nothing at all {[],[],_Ecs,_Rest} -> error; %No number data {[],".",_Ecs,_Rest} -> error; %Only "empty" fraction {_,_,[_E],_Rest} -> error; %Only "empty" exponent {Hcs,Fcs,Ecs,_Rest} -> DW = list_to_integer("0" ++ Hcs), DF = dec_number_fraction(Fcs, DW), Dnum = dec_number_exponent(Ecs, DF), {ok,Dnum} end, case Result of {ok,Number} -> {token,{'NUMERAL',TokenLine,Number}}; error -> number_token_error(TokenChars) end. number_token_error(Tcs) -> {error,"malformed number near '" ++ Tcs ++ "'"}. dec_number_split(Tcs0) -> Digit = fun (C) -> ?DIGIT(C) end, %% The whole number characters. {Hcs,Tcs1} = lists:splitwith(Digit, Tcs0), %% The fraction characters. {Fcs,Tcs2} = dec_number_split_fraction(Tcs1), %% The exponent characters. {Ecs,Rest} = dec_number_split_exponent(Tcs2), {Hcs,Fcs,Ecs,Rest}. dec_number_split_fraction([$. | Fcs0]) -> {Fcs1,Frest} = lists:splitwith(fun (C) -> ?DIGIT(C) end, Fcs0), {[$.|Fcs1],Frest}; dec_number_split_fraction(Tcs) -> {[],Tcs}. dec_number_split_exponent([P | Pcs0]) when P =:= $e ; P =:= $E -> Digit = fun (C) -> ?DIGIT(C) end, case Pcs0 of [S | Pcs1] when S =:= $+ ; S =:= $- -> {Pcs2,Rest} = lists:splitwith(Digit, Pcs1), {[P,S|Pcs2],Rest}; Pcs1 -> {Pcs2,Rest} = lists:splitwith(Digit, Pcs1), {[P|Pcs2],Rest} end; dec_number_split_exponent(Tcs) -> {[],Tcs}. dec_number_fraction(".", DW) -> float(DW); dec_number_fraction([$. | Fcs], DW) -> DW + list_to_float("0." ++ Fcs); dec_number_fraction([], DW) -> DW. dec_number_exponent([_E | Ecs], DF) -> DF * math:pow(10, list_to_integer(Ecs)); dec_number_exponent([], DF) -> DF. %% hex_number_token(TokenChars, TokenLine) %% {token,{'NUMERAL',TokenLine,Float}} | {error,E}. %% Build either an integer or a float from a hexadecimal number %% string. We first collect the specific number section, then we %% create the number. This makes it easier to keep track of which %% sections we need to make Lua compliant Luerl numbers, as Lua has %% some very "specific" handling. %% %% 0[xX]\.{H}+([pP][-+]?{D}+)?{NAME}* : %% 0[xX]{H}+\.?{H}*([pP][-+]?{D}+)?{NAME}* hex_number_token([$0,X|TokenChars], TokenLine) -> %% io:format("hnt ~p\n", [hex_number_split(TokenChars)]), Result = case hex_number_split(TokenChars) of %% If there is anything after the number sections then %% it is an error! {_,_,_,Rest} when Rest =/= [] -> error; {[],[],[],_Rest} -> error; %Nothing at all {[],[],_Ecs,_Rest} -> error; %No number data {[],".",_Ecs,_Rest} -> error; %Only "empty" fraction {_,_,[_P],_Rest} -> error; %Only "empty" exponent {Hcs,Fcs,Ecs,_Rest} -> HW = list_to_integer("0" ++ Hcs, 16), HF = hex_number_fraction(Fcs, HW), Hnum = hex_number_exponent(Ecs, HF), {ok,Hnum} end, case Result of {ok,Number} -> {token,{'NUMERAL',TokenLine,Number}}; error -> number_token_error([$0,X|TokenChars]) end. hex_number_split(Tcs0) -> Hex = fun (C) -> ?HEX(C) end, %% Digit = fun (C) -> ?DIGIT(C) end, %% The whole number characters. {Hcs,Tcs1} = lists:splitwith(Hex, Tcs0), %% The fraction characters. {Fcs,Tcs2} = hex_number_split_fraction(Tcs1), %% The exponent characters. {Ecs,Rest} = hex_number_split_exponent(Tcs2), {Hcs,Fcs,Ecs,Rest}. hex_number_split_fraction([$. | Fcs0]) -> {Fcs1,Frest} = lists:splitwith(fun (C) -> ?HEX(C) end, Fcs0), {[$.|Fcs1],Frest}; hex_number_split_fraction(Tcs) -> {[],Tcs}. hex_number_split_exponent([P | Pcs0]) when P =:= $p ; P =:= $P -> Digit = fun (C) -> ?DIGIT(C) end, case Pcs0 of [S | Pcs1] when S =:= $+ ; S =:= $- -> {Pcs2,Rest} = lists:splitwith(Digit, Pcs1), {[P,S|Pcs2],Rest}; Pcs1 -> {Pcs2,Rest} = lists:splitwith(Digit, Pcs1), {[P|Pcs2],Rest} end; hex_number_split_exponent(Tcs) -> {[],Tcs}. hex_number_fraction([$. | Fcs], HW) -> {HF,_} = hex_number_fraction(Fcs, 16.0, HW + 0.0), HF; hex_number_fraction([], HW) -> HW. hex_number_exponent([_P | Ecs], HF) -> HF * math:pow(2, list_to_integer(Ecs)); hex_number_exponent([], HF) -> HF. hex_number_fraction([C|Cs], Pow, SoFar) when C >= $0, C =< $9 -> hex_number_fraction(Cs, Pow*16.0, SoFar + (C - $0)/Pow); hex_number_fraction([C|Cs], Pow, SoFar) when C >= $a, C =< $f -> hex_number_fraction(Cs, Pow*16.0, SoFar + (C - $a + 10)/Pow); hex_number_fraction([C|Cs], Pow, SoFar) when C >= $A, C =< $F -> hex_number_fraction(Cs, Pow*16.0, SoFar + (C - $A + 10)/Pow); hex_number_fraction(Cs, _Pow, SoFar) -> {SoFar,Cs}. %% string_token(InputChars, Length, Line) -> %% {token,{'LITERALSTRING',Line,Cs}} | {error,Error}. %% Convert an input string into the corresponding string characters. %% We know that the input string is correct. string_token([Qc|Cs0], _Len, L) -> Cs1 = lists:droplast(Cs0), %Strip trailing quote %% io:format("st1 ~w ~w\n", [length(Cs1),Cs1]), try Bytes = string_chars(Cs1), %The bytes are encoded chars String = iolist_to_binary(Bytes), %% io:format("st2 ~w ~w\n", [byte_size(String),String]), {token,{'LITERALSTRING',L,String}} catch throw:{string_error,What} -> %Specific error message string_token_error(What, Qc); _:_ -> %General error message string_token_error("illegal string", Qc) end. string_token_error(What, Qc) -> {error,What ++ " near '" ++ [Qc] ++ "'"}. %% string_chars(Chars) %% chars(Chars) %% Return a list of UTF-8 encoded binaries and one byte unencoded %% characters. chars/1 is for external backwards compatibilty. chars(Cs) -> string_chars(Cs). string_chars(Cs) -> string_chars(Cs, []). string_chars([$\\ | Cs], Acc) -> string_bq_chars(Cs, Acc); string_chars([$\n | _], _Acc) -> throw(string_error); string_chars([C | Cs], Acc) -> string_chars(Cs, [C | Acc]); %% string_chars([C | Cs], Acc) when ?ASCII(C) -> %% string_chars(Cs, [C | Acc]); %% string_chars([C | Cs], Acc) -> %% case unicode:characters_to_binary([C]) of %% Bin when is_binary(Bin) -> %% string_chars(Cs, [Bin | Acc]); %% _Error -> %% throw(string_error) %% end; string_chars([], Acc) -> lists:reverse(Acc). %% string_bq_chars(Chars, Accumulator) %% Handle the backquotes characters. string_bq_chars([C1|Cs0], Acc) when ?DIGIT(C1) -> %1-3 decimal digits I1 = C1 - $0, %% Note here we "export" Byte and Cs1 (this is Erlang). case Cs0 of [C2,C3|Cs1] when ?DIGIT(C2), ?DIGIT(C3) -> Byte = 100 * I1 + 10 * (C2 - $0) + (C3 - $0), (Byte =< 255) orelse throw(string_error); [C2|Cs1] when ?DIGIT(C2) -> Byte = 10 * I1 + (C2 - $0); Cs1 -> Byte = I1 end, string_chars(Cs1, [Byte | Acc]); string_bq_chars([$x,C1,C2|Cs], Acc) -> %2 hex digits case ?HEX(C1) andalso ?HEX(C2) of true -> Byte = hex_val(C1)*16 + hex_val(C2), string_chars(Cs, [Byte|Acc]); false -> throw({string_error,"hexadecimal digit expected"}) end; string_bq_chars([$u,${|Cs], Acc) -> %Explicit utf-8 character string_bq_chars_utf8(Cs, 0, Acc); string_bq_chars([$z|Cs], Acc) -> %Skip whitespace string_chars(skip_space(Cs), Acc); string_bq_chars([C|Cs], Acc) -> case escape_char(C) of error -> throw({string_error,"invalid escape sequence"}); Esc -> string_chars(Cs, [Esc|Acc]) end; string_bq_chars([], Acc) -> Acc. string_bq_chars_utf8([C|Cs], Uchar, Acc) when ?HEX(C) -> string_bq_chars_utf8(Cs, Uchar*16 + hex_val(C), Acc); string_bq_chars_utf8([$}|Cs], Uchar, Acc) -> case unicode:characters_to_binary([Uchar]) of Bin when is_binary(Bin) -> string_chars(Cs, [Bin|Acc]); _Error -> throw({string_error,"UTF-8 value error"}) end; string_bq_chars_utf8(_Cs, _Uchar, _Acc) -> throw({string_error,"missing '}'"}). skip_space([$\s|Cs]) -> skip_space(Cs); skip_space(Cs) -> Cs. %% long_string_token(InputChars, Length, BracketLength, Line) -> %% {token,{'LITERALSTRING',Line,Cs}} | {error,Error}. long_string_token(Cs0, Len, BrLen, Line) -> %% Strip the brackets and remove first char if a newline. %% Note we "export" Cs1 here, (this is Erlang). case string:substr(Cs0, BrLen+1, Len - 2*BrLen) of [$\n | Cs1] -> Cs1; Cs1 -> Cs1 end, %% io:format("lst1 ~w ~w\n", [length(Cs1),Cs1]), try Bytes = long_string_chars(Cs1, []), %The bytes are encoded chars String = iolist_to_binary(Bytes), %% io:format("lst2 ~w ~w\n", [byte_size(String),String]), {token,{'LITERALSTRING',Line,String}} catch _:_ -> {error,"illegal long string"} end. long_string_chars([C | Cs], Acc) -> long_string_chars(Cs, [C|Acc]); %% long_string_chars([C | Cs], Acc) when ?ASCII(C) -> %% long_string_chars(Cs, [C|Acc]); %% long_string_chars([C | Cs], Acc) -> %This could be unicode %% case unicode:characters_to_binary([C]) of %% Bin when is_binary(Bin) -> %% long_string_chars(Cs, [Bin|Acc]); %% _Error -> %% throw(long_string_error) %% end; long_string_chars([], Acc) -> lists:reverse(Acc). hex_val(C) when C >= $0, C =< $9 -> C - $0; hex_val(C) when C >= $a, C =< $f -> C - $a + 10; hex_val(C) when C >= $A, C =< $F -> C - $A + 10. escape_char($a) -> 7; %\a = BELL escape_char($b) -> $\b; %\b = BS escape_char($f) -> $\f; %\f = FF escape_char($n) -> $\n; %\n = LF escape_char($r) -> $\r; %\r = CR escape_char($t) -> $\t; %\t = TAB escape_char($v) -> $\v; %\v = VT escape_char($\\) -> $\\; %\\ = BACKSLASH escape_char($") -> $"; %\" = STRING QUOTE escape_char($') -> $'; %\' = STRING QUOTE escape_char($\n) -> $\n; %\LF = LF escape_char($\r) -> $\n; %\RET = LF escape_char(_C) -> error. %Illegal %% is_keyword(Name) -> boolean(). %% Test if the name is a keyword. is_keyword(<<"and">>) -> true; is_keyword(<<"break">>) -> true; is_keyword(<<"do">>) -> true; is_keyword(<<"else">>) -> true; is_keyword(<<"elseif">>) -> true; is_keyword(<<"end">>) -> true; is_keyword(<<"false">>) -> true; is_keyword(<<"for">>) -> true; is_keyword(<<"function">>) -> true; is_keyword(<<"goto">>) -> true; is_keyword(<<"if">>) -> true; is_keyword(<<"in">>) -> true; is_keyword(<<"local">>) -> true; is_keyword(<<"nil">>) -> true; is_keyword(<<"not">>) -> true; is_keyword(<<"or">>) -> true; is_keyword(<<"repeat">>) -> true; is_keyword(<<"return">>) -> true; is_keyword(<<"then">>) -> true; is_keyword(<<"true">>) -> true; is_keyword(<<"until">>) -> true; is_keyword(<<"while">>) -> true; is_keyword(_) -> false. rvirding-luerl-772b25e/src/luerl_comp.hrl0000664000232200023220000000736115114130670021032 0ustar debalancedebalance%% Copyright (c) 2013-2019 Robert Virding %% %% 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. %% File : luerl_comp.hrl %% Author : Robert Virding %% Purpose : Internal LUA 5.2 compiler definitions. %% Common compiler information -record(cinfo, {lfile=[], %Lua file name vfile=[], %Virtual file name opts=[] %Compiler options }). %% Some useful macros. -define(IF(Test,True,False), case Test of true -> True; false -> False end). -define(WHEN_OPT(Opt,Opts,Fun), ?IF(member(Opt, Opts), Fun(), ok)). -define(DEBUG_PRINT(Format,Args,Opts), ?WHEN_OPT(debug_print, Opts, fun () -> io:fwrite(Format, Args) end)). %% Variable data. -record(vars, {local=[], %Local variables free=[], %Free variables used=[], %Used in sub blocks fused=[] %Used in sub-functions }). %% Define internal data macros. %% Statements. %% The line number here, 'l', can be a line number or annotation list. -record(assign_stmt, {l,vars,exps}). -record(call_stmt, {l,call}). -record(return_stmt, {l,exps}). -record(break_stmt, {l}). -record(block_stmt, {l, body=[], %Block body statements vars=none, %Variable info lsz=none, %Local frame size loc=not_used, %Local var block template esz=none, %Env frame size env=not_used, %Local env block template %%local=none, %Local variables locf=false}). %Local functions -record(while_stmt, {l,exp,body=[]}). -record(repeat_stmt, {l,body=[]}). -record(nfor_stmt, {l, var, %Loop variable init,limit,step, %The init, limit, step values body=[]}). %Loop body -record(gfor_stmt, {l, vars, %Loop variables gens, %Generators body=[]}). %Loop body -record(if_stmt, {l,tests=[],else_block}). -record(local_assign_stmt, {l,vars,exps}). -record(local_fdef_stmt, {l,var,func}). -record(expr_stmt, {l,exp}). %Pseudo stmt for expressions -record(block, {l, body=[], %Block body statements vars=none, %Variable info lsz=none, %Local frame size loc=not_used, %Local var block template esz=none, %Env frame size env=not_used, %Local env block template locf=false}). %% Expressions. %% The line number here, 'l', can be a line number or annotation list. -record(fdef, {l, pars=[], %Parameters body=[], %Function body statements vars=none, %Variable info lsz=none, %Local frame size loc=not_used, %Local var block template esz=none, %Env frame size env=not_used, %Local env block template %%local=none, %Local variables locf=false}). %Local function -record(lit, {l,val}). %Literal value -record(op, {l,op,args=[]}). -record(dot, {l,exp,rest}). -record(single, {l,exp}). -record(var, {l,name}). -record(fcall, {l,args=[]}). %Function call -record(mcall, {l,meth,args=[]}). %Method call -record(key, {l,key}). -record(tabcon, {l,fields=[]}). %Table constructor -record(efield, {l,val}). -record(kfield, {l,key,val}). %% Variable types. %% The line number here, 'l', can be a line number or annotation list. -record(lvar, {l,n,d,i}). %Local name, depth, index -record(evar, {l,n,d,i}). %Environment name, depth, index -record(gvar, {l,n}). %Global name rvirding-luerl-772b25e/src/luerl_lib_io.erl0000664000232200023220000000257115114130670021324 0ustar debalancedebalance%% Copyright (c) 2013-2020 Robert Virding %% %% 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. %% File : luerl_lib_io.erl %% Author : Robert Virding %% Purpose : The io library for Luerl. %% This is a quick hack to get io working. It will be improved in time. -module(luerl_lib_io). -include("luerl.hrl"). ?MODULEDOC(false). -export([install/1,flush/3,write/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this install(St) -> luerl_heap:alloc_table(table(), St). %% table() -> [{FuncName,Function}]. table() -> [{<<"flush">>,#erl_mfa{m=?MODULE,f=flush}}, {<<"write">>,#erl_mfa{m=?MODULE,f=write}} ]. flush(_, _, St) -> {[true],St}. write(_, As, St) -> case luerl_lib:args_to_strings(As) of error -> badarg_error(write, As, St); Ss -> lists:foreach(fun (S) -> io:format("~s", [S]) end, Ss), {[#userdata{d=standard_io}],St} end. rvirding-luerl-772b25e/src/luerl_sandbox.erl0000664000232200023220000001715015114130670021524 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl_sandbox.erl %% Authors : Tyler Butchart %% Purpose : Reduction limiting luerl sandbox. -module(luerl_sandbox). -include("luerl.hrl"). ?MODULEDOC( """ Sandboxed execution environment for Luerl with reduction counting. This module provides a way to run Lua code with controlled execution, limiting the number of reductions (computational steps) to prevent excessive resource consumption. The `ControlFlags` is a map or keyword list which can contain the following fields ``` #{max_time => MaxTime, % 100 msecs max_reductions => MaxReds, % none spawn_opts => SpawnOpts} % [] ``` `MaxReds` limits the number of reductions (default no max) and `MaxTime` (default 100 msecs) limits the maximum time to run the string. `SpawnOpts` are spawn options to the process running the evaluation. The default values are shown as comments. Any other fields are ignored. """). -export([init/0,init/1,init/2, run/1,run/2,run/3,run/4,run/5]). -define(LUERL_GLOBAL, '_G'). -define(SANDBOXED_VALUE, sandboxed). -define(SANDBOXED_GLOBALS, [ [?LUERL_GLOBAL, io], [?LUERL_GLOBAL, file], [?LUERL_GLOBAL, os, execute], [?LUERL_GLOBAL, os, exit], [?LUERL_GLOBAL, os, getenv], [?LUERL_GLOBAL, os, remove], [?LUERL_GLOBAL, os, rename], [?LUERL_GLOBAL, os, tmpname], [?LUERL_GLOBAL, package], [?LUERL_GLOBAL, load], [?LUERL_GLOBAL, loadfile], [?LUERL_GLOBAL, require], [?LUERL_GLOBAL, dofile], [?LUERL_GLOBAL, load], [?LUERL_GLOBAL, loadfile], [?LUERL_GLOBAL, loadstring] ]). %% The controlflags for controlling the execition. -type controlflags() :: #{atom() := term()} | [{atom(),term()}]. -type sandboxtable() :: [[atom()]]. -define(MAX_TIME, 100). %% Define IS_MAP/1 macro for is_map/1 bif. -ifdef(HAS_MAPS). -define(IS_MAP(T), is_map(T)). -else. -define(IS_MAP(T), false). -endif. ?DOC( """ Create a new Luerl state with the standard sandboxing. """). -spec init() -> LuaState when LuaState :: luerlstate(). init() -> init(luerl:init()). %% init([, State|TablePaths[, TablePaths]]) -> State ?DOC( """ Take an existing Luerl state and run the default sandboxing on it. Or create a new Luerl state and run a sandboxing table on it. """). -spec init(LuaState) -> LuaState when LuaState :: luerlstate() ; (TablePaths) -> LuaState when LuaState :: luerlstate(), TablePaths :: sandboxtable(). init(TablePaths) when is_list(TablePaths) -> init(luerl:init(), TablePaths); init(St) -> init(St, ?SANDBOXED_GLOBALS). ?DOC( """ Take an existing Luerl state and run the `TablePaths` on it to control the sandboxing. """). -spec init(LuaState, TablePaths) -> LuaState when LuaState :: luerlstate(), TablePaths :: [[atom()]]. init(St, []) -> luerl:gc(St); init(St0, [Path|Tail]) -> {ok,St1} = luerl:set_table_keys_dec(Path, ?SANDBOXED_VALUE, St0), init(St1, Tail). %% The default flags for running the sandboxed process. default_flags() -> [{max_time, ?MAX_TIME}, {max_reductions, none}, {spawn_opts, []}]. %% run(String|Binary) -> {Term,State} | {error,Term}. %% run(String|Binary, State) -> {Term,State} | {error,Term}. %% run(String|Binary, Flags, State) -> {Term,State} | {error,Term}. %% The new interface. ?DOC( """ Run the Lua expression controlled by the default `ControlFlags` in a new `LuaState` with the default sandboxing. """). -spec run(Expression) -> {Reply,LuaState} when Expression :: string(), LuaState :: luerlstate(), Reply :: {ok,Result,LuaState} | Error, Result :: luerldata(), Error :: term(). run(S) -> run(S, init()). ?DOC( """ Run the Lua expression controlled by the default `ControlFlags` in the `LuaState`. """). -spec run(Expression, LuaState) -> {Reply,LuaState} when Expression :: string(), LuaState :: luerlstate(), Reply :: {ok,Result,LuaState} | Error, Result :: luerldata(), Error :: term(). run(S, St) -> do_run(S, default_flags(), St). ?DOC( """ Run the Lua expression controlled by the `ControlFlags` in the `LuaState`. """). -spec run(Expression, ControlFlags, LuaState) -> {Reply,LuaState} when Expression :: string(), ControlFlags :: controlflags(), LuaState :: luerlstate(), Reply :: {ok,Result,LuaState} | Error, Result :: luerldata(), Error :: term(). %% The new interface. run(S, Flags, St) when ?IS_MAP(Flags) -> run(S, maps:to_list(Flags), St); run(S, Flags, #luerl{}=St) when is_list(Flags) -> do_run(S, Flags ++ default_flags(), St); %% The old interface. run(S, St, MaxR) when is_integer(MaxR) -> run(S, St, MaxR, []); run(S, St, Flags) when is_list(Flags) -> run(S, St, 0, Flags). %% run(String|Binary|Form[, State[, MaxReductions|Flags[, Flags[, Timeout]]]]) -> {Term,State}|{error,Term} %% This is the old interface which still works. ?DOC( """ run(String, LuaState, MaxReds, SpawnOpts) """). ?DOC( #{deprecated => "Use `run/3`" } ). run(S, St, MaxR, Flags) -> run(S, St, MaxR, Flags, ?MAX_TIME). ?DOC( """ run(String, LuaState, MaxReds, SpawnOpts, Timeout) """). ?DOC( #{deprecated => "Use `run/3`" } ). run(S, St, 0, Opts, MaxT) -> %% Need to get the old no reductions to the new no reductions. run(S, St, none, Opts, MaxT); run(S, St, MaxR, Opts, MaxT) -> Flags = [{max_time,MaxT},{max_reductions,MaxR},{spawn_opts,Opts}], do_run(S, Flags, St). do_run(S, Flags, St) -> MaxT = proplists:get_value(max_time, Flags), Opts = proplists:get_value(spawn_opts, Flags), Runner = start(self(), S, Opts, St), case proplists:get_value(max_reductions, Flags) of none -> receive_response(Runner, MaxT); MaxR when is_integer(MaxR), MaxR > 0 -> case wait_reductions(Runner, MaxR) of {killed, R} -> {error, {reductions, R}}; ok -> receive_response(Runner, MaxT) end; _Other -> exit(badarg) end. start(Parent, S, Opts, St) -> spawn_opt(fun() -> try Reply = luerl:do(S, St), erlang:send(Parent, {self(), Reply}) catch error:Reason -> erlang:send(Parent, {self(), {error, Reason}}) end end, Opts). wait_reductions(Runner, MaxR) -> case process_info(Runner, reductions) of undefined -> %% The process has died. ok; {reductions, R} when R >= MaxR -> exit(Runner, kill), {killed, R}; {reductions, _} -> %% We only check every default MAX_TIME so we don't %% overload the runner process too much. receive after ?MAX_TIME -> ok end, wait_reductions(Runner, MaxR) end. receive_response(Runner, Timeout) -> receive {Runner, Reply} -> %% The runner has terminated. Reply; {error, Error} -> Error after Timeout -> %% Kill the runner as its time is up. exit(Runner, kill), {error, timeout} end. rvirding-luerl-772b25e/src/luerl_comp_locf.erl0000664000232200023220000002131215114130670022022 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : luerl_comp_locf.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Does local function analysis. -module(luerl_comp_locf). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). -export([chunk/2]). -import(ordsets, [add_element/2,is_element/2,union/1,union/2, subtract/2,intersection/2]). %% chunk(Code, CompInfo) -> %% {ok,Code} | {ok,Code,Warnings} | {error,Error}. chunk(Code0, #cinfo{opts=Opts}=_Ci) -> {Code1,_,nul} = exp(Code0, nul), %No local state here! luerl_comp:debug_print(Opts, "cf: ~p\n", [Code1]), {ok,Code1}. %% stmt(Stmts, State) -> {Stmts,LocalFunc,State}. stmts([S0|Ss0], St0) -> {S1,Slocf,St1} = stmt(S0, St0), {Ss1,Sslocf,St2} = stmts(Ss0, St1), Locf = Slocf or Sslocf, {[S1|Ss1],Locf,St2}; stmts([], St) -> {[],false,St}. %% stmt(Stmt, State) -> {Stmt,LocalFunc,State}. stmt(#assign_stmt{}=A, St) -> assign_stmt(A, St); stmt(#call_stmt{}=C, St) -> call_stmt(C, St); stmt(#return_stmt{}=R, St) -> return_stmt(R, St); stmt(#break_stmt{}=B, St) -> {B,false,St}; stmt(#block_stmt{}=B, St) -> block_stmt(B, St); stmt(#while_stmt{}=W, St) -> while_stmt(W, St); stmt(#repeat_stmt{}=R, St) -> repeat_stmt(R, St); stmt(#if_stmt{}=If, St) -> if_stmt(If, St); stmt(#nfor_stmt{}=For, St) -> numfor_stmt(For, St); stmt(#gfor_stmt{}=For, St) -> genfor_stmt(For, St); stmt(#local_assign_stmt{}=L, St) -> local_assign_stmt(L, St); stmt(#local_fdef_stmt{}=L, St) -> local_fdef_stmt(L, St); stmt(#expr_stmt{}=E, St) -> expr_stmt(E, St). %% assign_stmt(Assign, State) -> {Assign,LocalFunc,State}. assign_stmt(#assign_stmt{vars=Vs0,exps=Es0}=A, St0) -> {Vs1,Vlocf,St1} = assign_loop(Vs0, St0), {Es1,Elocf,St2} = explist(Es0, St1), Locf = Vlocf or Elocf, {A#assign_stmt{vars=Vs1,exps=Es1},Locf,St2}. assign_loop([V0|Vs0], St0) -> {V1,Vlocf,St1} = var(V0, St0), {Vs1,Vslocf,St2} = assign_loop(Vs0, St1), Locf = Vlocf or Vslocf, {[V1|Vs1],Locf,St2}; assign_loop([], St) -> {[],false,St}. var(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,Elocf,St1} = prefixexp_first(Exp0, St0), {Rest1,Rlocf,St2} = var_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},Elocf or Rlocf,St2}; var(V, St) -> {V,false,St}. var_rest(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,Elocf,St1} = prefixexp_element(Exp0, St0), {Rest1,Rlocf,St2} = var_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},Elocf or Rlocf,St2}; var_rest(Exp, St) -> var_last(Exp, St). var_last(#key{key=Exp0}=K, St0) -> {Exp1,Elocf,St1} = exp(Exp0, St0), {K#key{key=Exp1},Elocf,St1}. %% call_stmt(Call, State) -> {Call,LocalFunc,State}. call_stmt(#call_stmt{call=Exp0}=C, St0) -> {Exp1,Locf,St1} = exp(Exp0, St0), {C#call_stmt{call=Exp1},Locf,St1}. %% return_stmt(Return, State) -> {Return,LocalFunc,State}. return_stmt(#return_stmt{exps=Es0}=R, St0) -> {Es1,Locf,St1} = explist(Es0, St0), {R#return_stmt{exps=Es1},Locf,St1}. %% block_stmt(Block, State) -> {Block,LocalFunc,State}. block_stmt(#block_stmt{body=Ss0}=B, St0) -> {Ss1,Sslocf,St1} = stmts(Ss0, St0), {B#block_stmt{body=Ss1,locf=Sslocf},Sslocf,St1}. %% do_block(Block, State) -> {Block,LocalFunc,State}. do_block(#block{body=Ss0}=B, St0) -> {Ss1,Sslocf,St1} = stmts(Ss0, St0), {B#block{body=Ss1,locf=Sslocf},Sslocf,St1}. %% while_stmt(While, State) -> {While,LocalFunc,State}. %% The test expression is done in the context of the surrounding %% block. while_stmt(#while_stmt{exp=E0,body=B0}=W, St0) -> {E1,Elocf,St1} = exp(E0, St0), {B1,Blocf,St2} = do_block(B0, St1), {W#while_stmt{exp=E1,body=B1},Elocf or Blocf,St2}. %% repeat_stmt(Repeat, State) -> {Repeat,LocalFunc,State}. %% The test expression is done in the context of the repeat block. repeat_stmt(#repeat_stmt{body=B0}=R, St0) -> {B1,Blocf,St1} = do_block(B0, St0), {R#repeat_stmt{body=B1},Blocf,St1}. %% if_stmt(If, State) -> {If,LocalFunc,State}. %% The block info includes anything from the test expressions even %% though we keep them separate. if_stmt(#if_stmt{tests=Ts0,else_block=E0}=If, St0) -> {Ts1,Tlocf,St1} = if_tests(Ts0, St0), {E1,Elocf,St2} = do_block(E0, St1), Locf = Tlocf or Elocf, {If#if_stmt{tests=Ts1,else_block=E1},Locf,St2}. if_tests([{E0,B0}|Ts0], St0) -> {E1,Elocf,St1} = exp(E0, St0), {B1,Blocf,St2} = do_block(B0, St1), {Ts1,Tslocf,St3} = if_tests(Ts0, St2), Locf = Elocf or Blocf or Tslocf, {[{E1,B1}|Ts1],Locf,St3}; if_tests([], St) -> {[],false,St}. %% numfor_stmt(For, State) -> {For,LocalFunc,State}. numfor_stmt(#nfor_stmt{init=I0,limit=L0,step=S0,body=B0}=For, St0) -> {[I1,L1,S1],Eslocf,St1} = explist([I0,L0,S0], St0), {B1,Blocf,St2} = do_block(B0, St1), Locf = Eslocf or Blocf, {For#nfor_stmt{init=I1,limit=L1,step=S1,body=B1},Locf,St2}. %% genfor_stmt(For, State) -> {For,LocalFunc,State}. genfor_stmt(#gfor_stmt{gens=Gs0,body=B0}=For, St0) -> {Gs1,Glocf,St1} = explist(Gs0, St0), {B1,Blocf,St2} = do_block(B0, St1), Locf = Glocf or Blocf, {For#gfor_stmt{gens=Gs1,body=B1},Locf,St2}. %% local_assign_stmt(Local, State) -> {Local,LocalFunc,State}. local_assign_stmt(#local_assign_stmt{exps=Es0}=L, St0) -> {Es1,Eslocf,St1} = explist(Es0, St0), {L#local_assign_stmt{exps=Es1},Eslocf,St1}. %% local_fdef_stmt(Local, State) -> {Local,LocalFunc,State}. local_fdef_stmt(#local_fdef_stmt{func=F0}=L, St0) -> {F1,_,St1} = functiondef(F0, St0), %Don't care what's in func {L#local_fdef_stmt{func=F1},true,St1}. %% expr_stmt(Expr, State) -> {Expr,LocalFunc,State}. %% The expression pseudo statement. This will return a single value. expr_stmt(#expr_stmt{exp=Exp0}=E, St0) -> {Exp1,Locf,St1} = exp(Exp0, St0), {E#expr_stmt{exp=Exp1},Locf,St1}. %% explist(Exprs, State) -> {Exprs,LocalFunc,State}. %% exp(Expr, State) -> {Expr,LocalFunc,State}. explist([E0|Es0], St0) -> {E1,Elocf,St1} = exp(E0, St0), {Es1,Eslocf,St2} = explist(Es0, St1), {[E1|Es1],Elocf or Eslocf,St2}; explist([], St) -> {[],false,St}. %No expressions at all exp(#lit{}=L, St) -> {L,false,St}; %Nothing to do exp(#fdef{}=F0, St0) -> {F1,_,St1} = functiondef(F0, St0), %Don't care what's in func {F1,true,St1}; exp(#op{args=Es0}=Op, St0) -> {Es1,Eslocf,St1} = explist(Es0, St0), {Op#op{args=Es1},Eslocf,St1}; exp(#tabcon{fields=Fs0}=T, St0) -> {Fs1,Tlocf,St1} = tableconstructor(Fs0, St0), {T#tabcon{fields=Fs1},Tlocf,St1}; exp(E, St) -> prefixexp(E, St). prefixexp(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,Elocf,St1} = prefixexp_first(Exp0, St0), {Rest1,Rlocf,St2} = prefixexp_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},Elocf or Rlocf,St2}; prefixexp(Exp, St) -> prefixexp_first(Exp, St). prefixexp_first(#single{exp=E0}=S, St0) -> {E1,Elocf,St1} = exp(E0, St0), {S#single{exp=E1},Elocf,St1}; prefixexp_first(V, St) -> {V,false,St}. prefixexp_rest(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,Elocf,St1} = prefixexp_element(Exp0, St0), {Rest1,Rlocf,St2} = prefixexp_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},Elocf or Rlocf,St2}; prefixexp_rest(Exp, St) -> prefixexp_element(Exp, St). prefixexp_element(#key{key=E0}=K, St0) -> {E1,Elocf,St1} = exp(E0, St0), {K#key{key=E1},Elocf,St1}; prefixexp_element(#fcall{args=As0}=F, St0) -> {As1,Aslocf,St1} = explist(As0, St0), {F#fcall{args=As1},Aslocf,St1}; prefixexp_element(#mcall{args=As0}=M, St0) -> {As1,Aslocf,St1} = explist(As0, St0), {M#mcall{args=As1},Aslocf,St1}. %% functiondef(Func, State) -> {Func,LocalFunc,State}. %% We return if there are any internal function definitions within %% the function. functiondef(#fdef{body=Ss0}=F, St0) -> {Ss1,Sslocf,St1} = stmts(Ss0, St0), {F#fdef{body=Ss1,locf=Sslocf},Sslocf,St1}. %% tableconstructor(Fields, State) -> {Fields,LocalFunc,State}. tableconstructor(Fs0, St0) -> Fun = fun (#efield{val=V0}=F, {Locf,S0}) -> {V1,Vlocf,S1} = exp(V0, S0), {F#efield{val=V1},{Locf or Vlocf,S1}}; (#kfield{key=K0,val=V0}=F, {Locf,S0}) -> {K1,Klocf,S1} = exp(K0, S0), {V1,Vlocf,S2} = exp(V0, S1), {F#kfield{key=K1,val=V1},{Locf or Klocf or Vlocf,S2}} end, {Fs1,{Locf,St1}} = lists:mapfoldl(Fun, {false,St0}, Fs0), {Fs1,Locf,St1}. rvirding-luerl-772b25e/src/luerl_instrs.hrl0000664000232200023220000000602015114130670021405 0ustar debalancedebalance%% Copyright (c) 2019 Robert Virding %% %% 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. %% File : luerl_instrs.hrl %% Author : Robert Virding %% Purpose : Internal LUA 5.3 instructions. %% Expression instructions. -define(PUSH_LIT(L), {push_lit,L}). -define(PUSH_LVAR(D,I), {push_lvar,D,I}). -define(PUSH_EVAR(D, I), {push_evar,D,I}). -define(PUSH_GVAR(K), {push_gvar,K}). -define(PUSH_LAST_LIT(L), {push_last_lit,L}). %[?PUSH_LIT,?MULTIPLE] -define(PUSH_LAST_LVAR(D,I), {push_last_lvar,D,I}). -define(PUSH_LAST_EVAR(D, I), {push_last_evar,D,I}). -define(PUSH_LAST_GVAR(K), {push_last_gvar,K}). -define(STORE_LVAR(D, I), {store_lvar,D,I}). -define(STORE_EVAR(D, I), {store_evar,D,I}). -define(STORE_GVAR(K), {store_gvar,K}). -define(GET_KEY, get_key). %Acc = Stk[Acc] -define(GET_LIT_KEY(K), {get_lit_key,K}). %[?PUSH_LIT(K),?GET_KEY] -define(SET_KEY, set_key). %Stk[ -define(SET_LIT_KEY(K), {set_lit_key,K}). %[?PUSH_LIT(K),?SET_KEY] -define(SINGLE, single). %Ensure single value -define(MULTIPLE, multiple). %Ensure multiple value -define(BUILD_TAB(Fc, I), {build_tab,Fc,I}). -define(FCALL, fcall). -define(TAIL_FCALL, tail_fcall). -define(MCALL(M), {mcall,M}). -define(TAIL_MCALL(M), {tail_mcall,M}). -define(OP(Op,Ac), {op,Op,Ac}). -define(PUSH_FDEF(Anno, Lsz, Esz, Pars, Is), {push_fdef,Anno,Lsz,Esz,Pars,Is}). -define(PUSH_FDEF(FnRef), {push_fdef,FnRef}). %% Control instructions. -define(BLOCK(Lsz, Esz, Is), {block,Lsz,Esz,Is}). -define(BLOCK_OPEN(Lsz, Esz), {block_open,Lsz,Esz}). -define(BLOCK_CLOSE, block_close). -define(WHILE(E, B), {while,E,B}). -define(WHILE_LOOP(Eis, Wis), {while_loop,Eis,Wis}). -define(REPEAT(B), {repeat,B}). -define(REPEAT_LOOP(B), {repeat_loop,B}). -define(AND_THEN(T), {and_then,T}). -define(OR_ELSE(T), {or_else,T}). -define(IF_TRUE(T), {if_true,T}). -define(IF(T, F), {'if',T,F}). -define(NFOR(V, B), {nfor,V,B}). -define(NFOR_LOOP(N, Limit, Step, Fis), {nfor_loop,N,Limit,Step,Fis}). -define(GFOR(Vs, B), {gfor,Vs,B}). -define(GFOR_CALL(Func, Data, Val, Fis), {gfor_call,Func,Data,Val,Fis}). -define(GFOR_LOOP(Func, Data, Fis), {gfor_loop,Func,Data,Fis}). -define(BREAK, break). -define(RETURN(Ac), {return,Ac}). %% Stack instructions. -define(PUSH, push). -define(POP, pop). -define(POP2, pop2). -define(SWAP, swap). -define(DUP, dup). -define(PUSH_VALS(Vc), {push_vals,Vc}). -define(POP_VALS(Vc), {pop_vals,Vc}). -define(POP_ARGS(Ac), {pop_args,Ac}). -define(PUSH_ARGS(Al), {push_args,Al}). %% Comment and line instructions. -define(COMMENT(C), {comment,C}). -define(CURRENT_LINE(L, File), {current_line,L,File}). rvirding-luerl-772b25e/src/luerl_io.erl0000664000232200023220000001206215114130670020472 0ustar debalancedebalance%% Copyright (c) 2025 Robert Virding %% %% 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. %% File : luerl_io.erl %% Author : Robert Virding %% Purpose : Some basic i/o functions for Luerl. -module(luerl_io). -include("luerl.hrl"). ?MODULEDOC( """ This module provides a standard of set io functions for Luerl. In the following description, many functions have an optional parameter `IoDevice`. If included, it must be the pid of a process which handles the IO protocols such as the IoDevice returned by `file:open/2`. Also in the functions handling files it can also be the name of the file which will then be opened. Note that Luerl, following Lua, does do any implicit UTF-8 encoding of input strings. """). -export([get_line/0,get_line/1,get_line/2,collect_line/2]). -export([scan_file/1,scan_file/2,parse_file/1,parse_file/2]). %% get_line() -> Data | {error,Error} | eof. %% Reads a line from the standard input (IoDevice), prompting it with %% Prompt. Doing it this way saves the input in history. We also make %% sure that any utf-8 encoding is done before we return the data. ?DOC( #{equiv => get_line(standard_io, '')}). get_line() -> get_line(standard_io, ''). ?DOC( #{equiv => get_line(standard_io, Prompt)}). get_line(Prompt) -> get_line(standard_io, Prompt). ?DOC( """ Read a line of text from `IoDevice` withe the prompt `Prompt`. We make sure that anu UTF-8 encoding is done before we return the data. """). get_line(IoDevice, Prompt) -> Get = io:request(IoDevice, {get_until,latin1,Prompt,luerl_io,collect_line,[]}), %% Make sure unicode codepoints ahve been expanded. case Get of Line when is_list(Line) -> unicode:characters_to_binary(Line); Other -> Other end. %% collect_line(OldStack, Data) -> {done,Result,Rest} | {more,NewStack}. collect_line(Stack, Data) -> case io_lib:collect_line(start, Data, latin1, ignored) of {stop,Result,Rest} -> {done,lists:reverse(Stack, Result),Rest}; MoreStack -> {more,MoreStack ++ Stack} end. %% scan_file(FileName|Fd) -> {ok,[Token]} | {error,Error}. %% scan_file(FileName|Fd, Line) -> {ok,[Token]} | {error,Error}. %% Scan a file returning the tokens found in the file. Handle errors %% consistently. ?DOC( #{equiv => scan_file(FileName, 1)}). scan_file(FileName) -> scan_file(FileName, 1). ?DOC( """ Scan the file `FileName` and return the tokens in it. `FileName` can also be an `IoDevice` of an already opened file. """). scan_file(FileName, Line) -> with_token_file(FileName, fun (Ts, _LastLine) -> {ok,Ts} end, Line). %% parse_file(FileName|Fd) -> {ok,[{Sexpr,Line}]} | {error,Error}. %% parse_file(FileName|Fd, Line) -> {ok,[{Sexpr,Line}]} | {error,Error}. %% Parse a file returning the chunk it contained. Handle errors %% consistently. ?DOC( #{equiv => parse_file(FileName, 1)}). parse_file(FileName) -> parse_file(FileName, 1). ?DOC( """ Parse the file `FileName` and return the chunk in it. `FileName` can also be an `IoDevice` of an already opened file. """). parse_file(FileName, Line) -> with_token_file(FileName, fun (Ts, LastLine) -> parse_tokens(Ts, LastLine) end, Line). parse_tokens(Tokens, _LastLine) -> luerl_parse:chunk(Tokens). %% with_token_file(FileName|Fd, DoFunc, Line) %% Open the file, scan all Luerl tokens and apply DoFunc on them. Note %% that a new file starts at line 1. with_token_file(Fd, Do, Line) when is_pid(Fd) -> with_token_file_fd(Fd, Do, Line); with_token_file(Name, Do, _Line) -> case file:open(Name, [read,{encoding,latin1}]) of {ok,Fd} -> %% Check if first line a script or Windows BOM, if so skip it. case io:get_line(Fd, '') of "#" ++ _ -> ok; %Skip line [239,187,191|_] -> file:position(Fd, 3); %Skip BOM _ -> file:position(Fd, bof) %Get it all end, with_token_file_fd(Fd, Do, 1); %Start at first valid line {error,Error} -> {error,{none,file,Error}} end. with_token_file_fd(Fd, Do, Line) -> %Called with a file descriptor Ret = case io:request(Fd, {get_until,latin1,'',luerl_scan,tokens,[Line]}) of {ok,Ts,LastLine} -> Do(Ts, LastLine); {eof,_}=Eof -> Eof; %This might occur. {error,Error,_} -> {error,Error} end, file:close(Fd), %Close the file Ret. % and return value rvirding-luerl-772b25e/src/Elixir.Luerl.Old.erl0000664000232200023220000001040215114130670021707 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_ex.erl %% Authors : Cees de Groot %% Purpose : Elixir-style wrappers for luerl_old.erl %% This module just contains functions that forward to luerl_old.erl, %% but place the VM State arguments in the first position rather than %% the last. This better matches Elixir conventions and allows for %% using the Elixir pipe operator '|>' to chain Luerl function calls. -module('Elixir.Luerl.Old'). -include("luerl.hrl"). ?MODULEDOC(""" Legacy Elixir API for Luerl This module provides Elixir-friendly wrappers for the luerl_old module with state as the first argument for better pipe operator usage. New code should use the Luerl module instead. """). -export([eval/2,evalfile/2, do/2,dofile/2, load/2,load/3, loadfile/2,loadfile/3, path_loadfile/2,path_loadfile/3,path_loadfile/4, load_module/3,load_module1/3, call/3,call_chunk/3, call_function/3,call_function1/3,function_list/2, call_method/3,call_method1/3,method_list/2, get_table/2,get_table1/2,set_table/3,set_table1/3,set_table1/4, init/0,stop/1,gc/1, set_trace_func/2,clear_trace_func/1, set_trace_data/2,get_trace_data/1, get_stacktrace/1, externalize/1,internalize/1 ]). %% Encoding and decoding. -export([encode/2,encode_list/2,decode/2,decode_list/2]). eval(St, Chunk) -> luerl_old:eval(Chunk, St). evalfile(St, Path) -> luerl_old:evalfile(Path, St). do(St, S) -> luerl_old:do(S, St). dofile(St, Path) -> luerl_old:dofile(Path, St). load(St, Bin) -> luerl_old:load(Bin, St). load(St, Bin, Opts) -> luerl_old:load(Bin, Opts, St). loadfile(St, Name) -> luerl_old:loadfile(Name, St). loadfile(St, Name, Opts) -> luerl_old:loadfile(Name, Opts, St). path_loadfile(St, Name) -> luerl_old:path_loadfile(Name, St). path_loadfile(St, Dirs, Name) -> luerl_old:path_loadfile(Dirs, Name, St). path_loadfile(St, Dir, Name, Opts) -> luerl_old:path_loadfile(Dir, Name, Opts, St). load_module(St, Fp, Mod) -> luerl_old:load_module(Fp, Mod, St). load_module1(St, Fp, Mod) -> luerl_old:load_module1(Fp, Mod, St). init() -> luerl_old:init(). call(St, C, As) -> luerl_old:call(C, As, St). call_chunk(St, C, As) -> luerl_old:call_chunk(C, As, St). call_function(St, Fp, As) -> luerl_old:call_function(Fp, As, St). call_function1(St, Lfp, Las) -> luerl_old:call_function1(Lfp, Las, St). function_list(St, Ks) -> luerl_old:function_list(Ks, St). call_method(St, Fp, As) -> luerl_old:call_method(Fp, As, St). call_method1(St, Fp, Las) -> luerl_old:call_method1(Fp, Las, St). method_list(St, Ks) -> luerl_old:method_list(Ks, St). get_table(St, Fp) -> luerl_old:get_table(Fp, St). get_table1(St, Fp) -> luerl_old:get_table1(Fp, St). set_table(St, Fp, V) -> luerl_old:set_table(Fp, V, St). set_table1(St, Lfp, Lv) -> luerl_old:set_table1(Lfp, Lv, St). set_table1(St, Tab, Key, Lv) -> luerl_old:set_table1(Tab, Key, Lv, St). stop(St) -> luerl_old:stop(St). gc(St) -> luerl_old:gc(St). set_trace_func(St, Func) -> luerl_old:set_trace_func(Func, St). clear_trace_func(St) -> luerl_old:clear_trace_func(St). get_trace_data(St) -> luerl_old:get_trace_data(St). set_trace_data(St, Tdata) -> luerl_old:set_trace_data(Tdata, St). get_stacktrace(St) -> luerl_old:get_stacktrace(St). encode_list(St, Ts) -> luerl_old:encode_list(Ts, St). encode(St, V) -> luerl_old:encode(V, St). decode_list(St, Lts) -> luerl_old:decode_list(Lts, St). decode(St, V) -> luerl_old:decode(V, St). externalize(St) -> luerl_old:externalize(St). internalize(St) -> luerl_old:internalize(St). rvirding-luerl-772b25e/src/luerl.erl0000664000232200023220000010124515114130670020005 0ustar debalancedebalance%% Copyright (c) 2020-2025 Robert Virding %% %% 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. %% File : luerl.erl %% Authors : Robert Virding %% Purpose : The basic LUA 5.4 interface. -module(luerl). -include("luerl.hrl"). %% Use normal strings as it is easier to use " and make it readable %% with both old and new string parsing. ?MODULEDOC( "Luerl is an implementation of Lua 5.3 written in Erlang. This is the main public API module for interfacing with Luerl. The `LuaState` parameter is the state of a Lua VM instance. It must be created with the `init/0` call and be threaded from one call to the next. Note that Luerl, following Lua, does do any implicit UTF-8 encoding of input strings. This means that all strings given as arguments to the calls or the strings to evaluate with `do/3` or `do_dec/3` need to have already been UTF-8 encoded. This can be quite easily do with the `~` or `~b` sigils. For example `luerl:do(~b\"return 'árvíztűrő tükörfúrógép'\", St0)` or `luerl:do(~\"return 'árvíztűrő tükörfúrógép'\", St0)`"). %% Basic user API to luerl. -export([init/0,gc/1, load/2,load/3,loadfile/2,loadfile/3, path_loadfile/2,path_loadfile/3,path_loadfile/4, load_module/3,load_module_dec/3, do/2,do_dec/2,do/3,do_dec/3, dofile/2,dofile/3,dofile_dec/2,dofile_dec/3, call/3,call_chunk/2,call_chunk/3, call_function/3,call_function_enc/3,call_function_dec/3, call_method/4,call_method_dec/4, get_table_keys/2,get_table_keys_dec/2, set_table_keys/3,set_table_keys_dec/3, get_table_key/3,set_table_key/4, get_stacktrace/1 ]). %% Tracing. -export([set_trace_func/2,clear_trace_func/1, set_trace_data/2,get_trace_data/1]). %% Encoding and decoding. -export([encode/2,encode_list/2,decode/2,decode_list/2]). %% Helping with storing VM state -export([externalize/1,internalize/1]). %% Storing and retrieving private data -export([put_private/3,get_private/2,delete_private/2]). ?DOC( """ Create a new Lua state which is a fresh Lua VM instance. """). -spec init() -> LuaState when LuaState :: luerlstate(). init() -> luerl_emul:init(). ?DOC( """ Runs the garbage collector on a state and returns the new state. """). -spec gc(LuaState) -> LuaState when LuaState :: luerlstate(). gc(St) -> luerl_heap:gc(St). %% set_trace_func(TraceFunction, State) -> State. %% clear_trace_func(State) -> State. %% get_trace_data(State) -> TraceData. %% set_trace_data(TraceData, State) -> State. %% Set the trace function and access the trace data. ?DOC( """ Set the trace function. """). ?DOC( #{group => <<"Trace Control functions">>} ). -spec set_trace_func(Function, LuaState) -> LuaState when Function :: fun(), LuaState :: luerlstate(). set_trace_func(Tfunc, St) -> St#luerl{trace_func=Tfunc}. ?DOC( """ Clear the trace function. """). ?DOC( #{group => <<"Trace Control functions">>} ). -spec clear_trace_func(LuaState) -> LuaState when LuaState :: luerlstate(). clear_trace_func(St) -> St#luerl{trace_func=none}. ?DOC( """ Get the current trace data. """). ?DOC( #{group => <<"Trace Control functions">>} ). -spec get_trace_data(LuaState) -> TraceData when LuaState :: luerlstate(), TraceData :: term(). get_trace_data(St) -> St#luerl.trace_data. ?DOC( """ Set the trace data. """). ?DOC( #{group => <<"Trace Control functions">>} ). -spec set_trace_data(TraceData, LuaState) -> LuaState when LuaState :: luerlstate(), TraceData :: term(). set_trace_data(Tdata, St) -> St#luerl{trace_data=Tdata}. %% load(String|Binary, State) -> {ok,FuncRef,NewState}. %% load(String|Binary, Options, State) -> {ok,FuncRef,NewState}. ?DOC( #{equiv => load(LuaChunk, [return], LuaState)} ). ?DOC( #{group => <<"Load Code functions">>} ). -spec load(LuaCode, LuaState) -> {ok,Function,LuaState} | CompileError when LuaCode :: binary() | string(), Function :: #funref{}, LuaState :: luerlstate(), CompileError :: {error,[term()],[term()]}. load(Bin, St) -> load(Bin, [return], St). ?DOC( """ Parse Lua chunk code as string or binary, and return a compiled chunk function. """). ?DOC( #{group => <<"Load Code functions">>} ). -spec load(LuaCode, CompileOptions, LuaState) -> {ok,Function,LuaState} | CompileError when LuaCode :: binary() | string(), CompileOptions :: [term()], LuaState :: luerlstate(), Function :: #funref{}, CompileError :: {error,[term()],[term()]}. load(Bin, Opts, St) when is_binary(Bin) -> load(binary_to_list(Bin), Opts, St); load(Str, Opts, St0) -> case luerl_comp:string(Str, Opts) of {ok,Chunk} -> {FunRef,St1} = luerl_emul:load_chunk(Chunk, St0), {ok,FunRef,St1}; Error -> %Compile error Error end. %% loadfile(FileName, State) -> {ok,FuncRef,NewState}. %% loadfile(FileName, Options, State) -> {ok,FuncRef,NewState}. ?DOC( #{equiv => loadfile(FileName, [return], LuaState)}). ?DOC( #{group => <<"Load Code functions">>} ). -spec loadfile(FileName, LuaState) -> {ok,Function,LuaState} | CompileError when FileName :: string(), LuaState :: luerlstate(), Function :: #funref{}, CompileError :: {error,[term()],[term()]}. loadfile(Name, St) -> loadfile(Name, [return], St). ?DOC( """ Parse a Lua file, and return a compiled chunk function. """). ?DOC( #{group => <<"Load Code functions">>} ). -spec loadfile(FileName, CompileOptions, LuaState) -> {ok,Function,LuaState} | CompileError when FileName :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Function :: #funref{}, CompileError :: {error,[term()],[term()]}. loadfile(Name, Opts, St0) -> case luerl_comp:file(Name, Opts) of {ok,Chunk} -> {Func,St1} = luerl_emul:load_chunk(Chunk, St0), {ok,Func,St1}; Error -> Error end. %% path_loadfile(FileName, State) -> {ok,Function,FullName,State}. %% path_loadfile(Path, FileName, State) -> {ok,Function,FullName,State}. %% path_loadfile(Path, FileName, Options, State) -> %% {ok,Function,FullName,State}. ?DOC( """ Calls `path_loadfile/4` with Path set the value of `LUA_LOAD_PATH` and `CompileOptions` set to `[return]`. """). ?DOC( #{group => <<"Load Code functions">>} ). -spec path_loadfile(FileName, LuaState) -> {ok,Function,FullName,LuaState} | CompileError when FileName :: string(), LuaState :: luerlstate(), Function :: #funref{}, FullName :: string(), CompileError :: {error,[term()],term()}. path_loadfile(Name, St) -> Path = case os:getenv("LUA_LOAD_PATH") of false -> []; %You get what you asked for Env -> %% Get path separator depending on os type. Sep = case os:type() of {win32,_} -> ";"; %Windows _ -> ":" %Unix end, string:tokens(Env, Sep) %Split into path list end, path_loadfile(Path, Name, [return], St). ?DOC( #{equiv => path_loadfile(Path, FileName, [return], LuaState)}). ?DOC( #{group => <<"Load Code functions">>} ). -spec path_loadfile(Path, FileName, LuaState) -> {ok,Function,FullName,LuaState} | CompileError when Path :: [string()], FileName :: string(), LuaState :: luerlstate(), Function :: #funref{}, FullName :: string(), CompileError :: {error,[term()],[term()]}. path_loadfile(Dirs, Name, St) -> path_loadfile(Dirs, Name, [return], St). ?DOC( """ Search down a `Path` to find the Lua file and return a compiled ('form'). """). ?DOC( #{group => <<"Load Code functions">>} ). -spec path_loadfile(Path, FileName, CompileOptions, LuaState) -> {ok,Function,FullName,LuaState} | CompileError when Path :: list(string()), FileName :: string(), FullName :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Function :: #funref{}, CompileError :: {error,[term()],[term()]}. path_loadfile([Dir|Dirs], Name, Opts, St0) -> Full = filename:join(Dir, Name), case loadfile(Full, Opts, St0) of {ok,Func,St1} -> {ok,Func,Full,St1}; {error,[{_,_,enoent}],_} -> %Couldn't find the file path_loadfile(Dirs, Name, St0); Error -> Error end; path_loadfile([], _, _, _) -> {error,[{none,file,enoent}],[]}. %% load_module(KeyPath, ErlangModule, State) -> State. %% load_module_dec(DecodedTablePath, ModuleName, State) -> State. ?DOC( """ Load `ErlangModule` and install its table at `KeyPath` in the LuaTable which is **NOT** automatically Lua encoded. """). ?DOC( #{group => <<"Load Code functions">>} ). -spec load_module(KeyPath, ErlangModule, LuaState) -> LuaState when KeyPath :: [luerldata()], ErlangModule :: atom(), LuaState :: luerlstate(). load_module([_|_] = Lfp, Mod, St0) -> {Tab,St1} = Mod:install(St0), luerl_emul:set_table_keys(Lfp, Tab, St1); load_module(_, _, _) -> error(badarg). ?DOC( """ Load `ErlangModule` and install its table at `KeyPath` in the LuaTable which is automatically Lua encoded. """). ?DOC( #{group => <<"Load Code functions">>} ). -spec load_module_dec(KeyPath, ErlangModule, LuaState) -> LuaState when KeyPath :: [term()], ErlangModule :: atom(), LuaState :: luerlstate(). load_module_dec([_|_] = Dfp, Mod, St0) -> {Efp,St1} = encode_list(Dfp, St0), load_module(Efp, Mod, St1); load_module_dec(_, _, _) -> error(badarg). %% luerl:do(String|Binary|Form, State) -> %% luerl:do(String|Binary|Form, CompileOptions, State) -> %% {ok,Result,NewState} | {lua_error,Error,State}. ?DOC( #{equiv => do(Expression, [return], LuaState)} ). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec do(Expression, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when Expression :: string(), LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. do(S, St) -> do(S, [return], St). ?DOC( """ Compile a Lua expression string, evaluate it and return its result, which is **NOT** decoded, and the new Lua State. """). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec do(Expression, CompileOptions, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when Expression :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. do(S, Opts, St0) -> case load(S, Opts, St0) of {ok,FuncRef,St1} -> call_function(FuncRef, [], St1); Error -> Error end. ?DOC( #{equiv => do_dec(Expression, [return], LuaState)} ). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec do_dec(Expression, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when Expression :: string(), Result :: [term()], LuaState :: luerlstate(), LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. do_dec(S, St) -> do_dec(S, [return], St). ?DOC( """ Compile a Lua expression string, evaluate it and return its result, which is is decoded, and the new Lua State. """). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec do_dec(Expression, CompileOptions, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when Expression :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Result :: [term()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. do_dec(S, Opts, St0) -> case do(S, Opts, St0) of {ok,Eret,St1} -> {ok,decode_list(Eret, St1),St1}; Error -> Error end. %% luerl:dofile(FileName, State) -> %% luerl:dofile(FileName, CompileOptions, State) -> %% {ok,Result,NewState} | {lua_error,Error,State}. ?DOC( #{equiv => dofile(FileName, [return], LuaState)} ). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec dofile(FileName, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when FileName :: string(), LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. dofile(File, St) -> dofile(File, [], St). ?DOC( """ Load and execute the Lua code in the file and return its result which is **NOT** decoded, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). """). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec dofile(FileName, CompileOptions, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when FileName :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. dofile(File, Opts, St0) -> case loadfile(File, Opts, St0) of {ok,Func,St1} -> call_function(Func, [], St1); Error -> Error end. ?DOC( #{equiv => dofile_dec(FileName, [return], LuaState)} ). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec dofile_dec(FileName, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when FileName :: string(), Result :: [term()], LuaState :: luerlstate(), LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. dofile_dec(File, St) -> dofile_dec(File, [], St). ?DOC( """ Load and execute the Lua code in the file and return its result which is Lua decoded, and the new Lua State. Equivalent to doing luerl:do_dec("return dofile('FileName')"). """). ?DOC( #{group => <<"Evaluate Code functions">>} ). -spec dofile_dec(FileName, CompileOptions, LuaState) -> {ok,Result,LuaState} | LuaError | CompileError when FileName :: string(), CompileOptions :: [term()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}, CompileError :: {error,[term()],[term()]}. dofile_dec(File, Opts, St0) -> case dofile(File, Opts, St0) of {ok,Eret,St1} -> {ok,decode_list(Eret, St1),St1}; Error -> Error end. %% call(FuncRef, Args, State) -> %% call_chunk(FuncRef, State) -> %% call_chunk(FuncRef, Args, State) -> %% {ok,Return,State} | {lua_error,Error,State}. ?DOC( #{equiv => call_function(LuaFuncRef, Args, LuaState)} ). ?DOC( #{group => <<"Function/Method Call functions">>} ). call(LuaFuncRef, Args, LuaState) -> call_function(LuaFuncRef, Args, LuaState). ?DOC( #{equiv => call_function(LuaFuncRef, [], LuaState)} ). ?DOC( #{group => <<"Function/Method Call functions">>} ). call_chunk(LuaFuncRef, LuaState) -> call_function(LuaFuncRef, [], LuaState). ?DOC( #{equiv => call_function(LuaFuncRef, Args, LuaState)} ). ?DOC( #{group => <<"Function/Method Call functions">>} ). call_chunk(LuaFuncRef, Args, LuaState) -> call_function(LuaFuncRef, Args, LuaState). %% call_function(LuaFuncRef | LuaTablePath, Args, State) -> %% {ok,LuaReturn,State} | {lua_error,Error,State}. ?DOC( """ Call a function already defined in the state. `LuaFuncReaf` and `Args` are **NOT** automatically encoded and `Result` is **NOT** automatically decoded. """). ?DOC( #{group => <<"Function/Method Call functions">>} ). -spec call_function(LuaFuncRef, Args, LuaState) -> {ok,Result,LuaState} | LuaError when LuaFuncRef :: [luerldata()] | luerldata(), Args :: [luerldata()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}. call_function(Epath, Args, St0) when is_list(Epath) -> {ok,Efunc,St1} = get_table_keys(Epath, St0), call_function(Efunc, Args, St1); call_function(Func, Args, St0) -> try {Ret,St1} = luerl_emul:functioncall(Func, Args, St0), {ok,Ret,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. %% call_function_enc(DecodedFuncRef, Args, State) -> %% {ok,LuaReturn,State} | {lua_error,Error,State}. ?DOC( """ Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath` and `Args` are automatically encoded, while `Result` is **NOT** automatically decoded. """). ?DOC( #{group => <<"Function/Method Call functions">>} ). -spec call_function_enc(KeyPath, Args, LuaState) -> {ok,Result,LuaState} | LuaError when KeyPath :: [term()], Args :: [term()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}. call_function_enc(Dtpath, Dargs, St0) -> {Epath,St1} = encode_list(Dtpath, St0), {Eargs,St2} = encode_list(Dargs, St1), call_function(Epath, Eargs, St2). %% call_function_dec(DecodedFuncRef, Args, State) -> %% {ok,DecodedReturn,State} | {lua_error,Error,State}. ?DOC( """ Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath` and `Args` are automatically encoded, while `Result` is automatically decoded. """). ?DOC( #{group => <<"Function/Method Call functions">>} ). -spec call_function_dec(KeyPath, Args, LuaState) -> {ok,Result,LuaState} | LuaError when KeyPath :: [term()], Args :: [term()], LuaState :: luerlstate(), Result :: [term()], LuaError :: {lua_error,term(),LuaState}. call_function_dec(Dtpath, Dargs, St0) -> case call_function_enc(Dtpath, Dargs, St0) of {ok,Eret,St1} -> {ok,decode_list(Eret, St1),St1}; LuaError -> LuaError end. %% call_method(LuaObject, Method, Args, State) -> %% {ok,Return,State} | {lua_error,Error,State}. %% call_method_dec(DecodedObject, Method, Args, State) -> %% {ok,DecodedReturn,State} | {lua_error,Error,State}. ?DOC( """ Call the `Method` in the `LuaTable` with the `Args`. The `LuaTable`, `Method` and `Args` are **NOT** automatically encoded and the `Result` is **NOT** automatically decoded """). ?DOC( #{group => <<"Function/Method Call functions">>} ). -spec call_method(LuaTable, Method, Args, LuaState) -> {ok,Result,LuaState} | LuaError when LuaTable :: #tref{}, Method :: luerldata(), Args :: [luerldata()], LuaState :: luerlstate(), Result :: [luerldata()], LuaError :: {lua_error,term(),LuaState}. call_method(Obj, Meth, Args, St0) -> try {Ret,St1} = luerl_emul:methodcall(Obj, Meth, Args, St0), {ok,Ret,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. ?DOC( """ Call the `Method` in the `Table` with the `Args`. The `Table`, `Method` and `Args` are automatically encoded and the `Result` is automatically decoded. """). ?DOC( #{group => <<"Function/Method Call functions">>} ). -spec call_method_dec(KeyPath, Method, Args, LuaState) -> {ok,Result,LuaState} | LuaError when KeyPath :: [term()], Method :: term(), Args :: [term()], LuaState :: luerlstate(), Result :: [term()], LuaError :: {lua_error,term(),LuaState}. ?DOC( #{group => <<"Function/Method Call functions">>} ). call_method_dec(Dobj, Dmeth, Dargs, St0) -> {ok,Eobj,St1} = get_table_keys_dec(Dobj, St0), {Emeth,St2} = encode(Dmeth, St1), {Eargs,St3} = encode_list(Dargs, St2), case call_method(Eobj, Emeth, Eargs, St3) of {ok,Eret,St4} -> {ok,decode_list(Eret, St4),St4}; LuaError -> LuaError end. %% get_table_keys(Keys, State) -> %% get_table_keys_dec(DecodedKeys, State) -> %% {ok,Return,State} | {lua_error,Error,State}. %% set_table_keys(Keys, Val, State) -> %% set_table_keys_dec(DecodedKeys, DecodedVal, State) -> %% {ok,State} | {lua_error,Error,State}. ?DOC( """ Gets a value inside the Lua state. `KeyPath` is **NOT** encoded and `Result` is **NOT** decoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec get_table_keys(KeyPath, LuaState) -> {ok,Result,LuaState} | LuaError when KeyPath :: [luerldata()], LuaState :: luerlstate(), Result :: luerldata(), LuaError :: {lua_error,term(),LuaState}. get_table_keys(Keys, St0) -> try {Eret,St1} = luerl_emul:get_table_keys(Keys, St0), {ok,Eret,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. ?DOC( """ Gets a value inside the Lua state. `KeyPath` is automatically encoded and `Result` is automatically decoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec get_table_keys_dec(KeyPath, LuaState) -> {ok,Result,LuaState} | LuaError when KeyPath :: [term()], LuaState :: luerlstate(), Result :: term(), LuaError :: {lua_error,term(),LuaState}. get_table_keys_dec(Dkeys, St0) -> {Ekeys,St1} = encode_list(Dkeys, St0), case get_table_keys(Ekeys, St1) of {ok,Eret,St2} -> {ok,decode(Eret, St2),St2}; LuaError -> LuaError end. ?DOC( """ Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec set_table_keys(KeyPath, Value, LuaState) -> {ok,LuaState} | LuaError when KeyPath :: [luerldata()], Value :: luerldata(), LuaState :: luerlstate(), LuaError :: {lua_error,term(),LuaState}. set_table_keys(Keys, Val, St0) -> try St1 = luerl_emul:set_table_keys(Keys, Val, St0), {ok,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. ?DOC( """ Sets a value inside the Lua state. `KeyPath` and `Value` are automatically encoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec set_table_keys_dec(KeyPath, Value, LuaState) -> {ok,LuaState} | LuaError when KeyPath :: [term()], Value :: term(), LuaState :: luerlstate(), LuaError :: {lua_error,term(),LuaState}. set_table_keys_dec(Dkeys, Dval, St0) -> {Ekeys,St1} = encode_list(Dkeys, St0), {Eval,St2} = encode(Dval, St1), set_table_keys(Ekeys, Eval, St2). %% get_table_key(Tab, Key, State) -> %% {ok,Value,State} | {lua_error,Error,State}. %% set_table_key(Tab, Key, Value, State) -> %% {ok,State} | {lua_error,Error,State}. ?DOC( """ Get the value of a key in a table. `Table`, `Key` are **NOT** encoded and the `Result` is **NOT** decoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec get_table_key(Table, Key, LuaState) -> {ok,Result,LuaState} | LuaError when Table :: luerldata(), Key :: luerldata(), LuaState :: luerlstate(), Result :: luerldata(), LuaError :: {lua_error,term(),LuaState}. get_table_key(Tab, Key, St0) -> try {Eret,St1} = luerl_emul:get_table_key(Tab, Key, St0), {ok,Eret,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. ?DOC( """ Set the value of a key in a table. `Table`, `Key` and `Value` are **NOT** encoded. """). ?DOC( #{group => <<"Lua Table Access functions">>} ). -spec set_table_key(Table, Key, Value, LuaState) -> {ok,LuaState} | LuaError when Table :: luerldata(), Key :: luerldata(), Value :: luerldata(), LuaState :: luerlstate(), LuaError :: {lua_error,term(),LuaState}. set_table_key(Tab, Key, Val, St0) -> try St1 = luerl_emul:set_table_key(Tab, Key, Val, St0), {ok,St1} catch error:{lua_error,_E,_St} = LuaErr -> LuaErr end. %% get_stacktrace(State) -> [{FuncName,[{file,FileName},{line,Line}]}]. ?DOC( """ Return a stack trace of the current call stack in the state. """). -spec get_stacktrace(LuaState) -> [FuncCall] when LuaState :: luerlstate(), FuncCall :: {FuncName,CallArgs,ExtraInfo}, FuncName :: atom(), ExtraInfo :: [{atom(),term()}], CallArgs :: [term()]. get_stacktrace(#luerl{cs=Stack}=St) -> Fun = fun (Frame, Acc) -> do_stackframe(Frame, Acc, St) end, {_,Trace} = lists:foldl(Fun, {1,[]}, Stack), lists:reverse(Trace). do_stackframe(#call_frame{func=Funref,args=Args}, {Line,Trace}, St) -> case Funref of #funref{} -> {Func,_} = luerl_heap:get_funcdef(Funref, St), Anno = Func#lua_func.anno, Name = case luerl_anno:get(name, Anno) of undefined -> <<"-no-name-">>; N -> N end, File = luerl_anno:get(file, Anno), {Line,[{Name,Args,[{file,File},{line,Line}]} | Trace]}; #erl_func{code=Fun} -> {module,Module} = erlang:fun_info(Fun, module), {name,Name} = erlang:fun_info(Fun, name), FileName = get_filename(Module), {Line,[{{Module,Name},Args,[{file,FileName}]} | Trace]}; #erl_mfa{m=M,f=F,a=A} -> FileName = get_filename(M), %% {Line,[{{M,F},{A,Args},[{file,FileName}]} | Trace]}; %% {Line,[{{M,F},[A | Args],[{file,FileName}]} | Trace]}; {Line,[{{M,F,A},Args,[{file,FileName}]} | Trace]}; Other -> {Line,[{Other,Args,[{file,<<"-no-file-">>},{line,Line}]} | Trace]} end; do_stackframe(#current_line{line=Line}, {_,Trace}, _St) -> {Line,Trace}; do_stackframe(#loop_frame{}, Acc, _St) -> %Ignore these Acc. get_filename(Mod) -> Comp = erlang:get_module_info(Mod, compile), case lists:keyfind(source, 1, Comp) of {source,FileName} -> BaseName = filename:basename(FileName), list_to_binary(BaseName); false -> %The compiler doesn't know <<"-no-file-">> end. %% Define IS_MAP/1 macro for is_map/1 bif. -ifdef(HAS_MAPS). -define(IS_MAP(T), is_map(T)). -else. -define(IS_MAP(T), false). -endif. %% encode_list([Term], State) -> {[LuerlTerm],State}. %% encode(Term, State) -> {LuerlTerm,State}. ?DOC( """ Encode a list of Erlang terms into their Luerl representations if possible. """). ?DOC( #{ group => <<"Encode/Decode Data functions">>} ). -spec encode_list([Term], LuaState) -> {[LuerlTerm],LuaState} when LuerlTerm :: term(), Term :: term(), LuaState :: luerlstate(). encode_list(Terms, LuaState) -> lists:mapfoldl(fun encode/2, LuaState, Terms). ?DOC( """ encode(Term, LuaState) Encode an Erlang term into its Luerl representation if possible. """). ?DOC( #{ group => <<"Encode/Decode Data functions">>} ). -spec encode(Term, LuaState) -> {LuerlTerm,LuaState} when LuerlTerm :: luerldata(), Term :: term(), LuaState :: luerlstate(). encode(nil, St) -> {nil,St}; encode(false, St) -> {false,St}; encode(true, St) -> {true,St}; encode(B, St) when is_binary(B) -> {B,St}; encode(A, St) when is_atom(A) -> {atom_to_binary(A, utf8),St}; encode(N, St) when is_number(N) -> {N,St}; %Integers and floats encode(F, St) when ?IS_MAP(F) -> encode(maps:to_list(F), St); encode(L, St0) when is_list(L) -> %% Encode the table elements in the list. EncTab = fun ({K0,V0}, {I,S0}) -> {K1,S1} = encode(K0, S0), {V1,S2} = encode(V0, S1), {{K1,V1},{I,S2}}; (V0, {I,S0}) -> {V1,S1} = encode(V0, S0), {{I,V1},{I+1,S1}} end, {Es,{_,St1}} = lists:mapfoldl(EncTab, {1,St0}, L), {T,St2} = luerl_heap:alloc_table(Es, St1), {T,St2}; %No more to do for now encode(F, St) when is_function(F, 2) -> F1 = fun(Args, State) -> F(Args, State) end, %% io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode(F, St) when is_function(F, 1) -> F1 = fun(Args, State) -> Res = F(Args), {Res,State} end, %% io:format("enc ~p\n", [#erl_func{code=F1}]), {#erl_func{code=F1}, St}; encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> %% io:format("enc ~p\n", [#erl_mfa{m=M,f=F,a=A}]), {#erl_mfa{m=M,f=F,a=A}, St}; encode({userdata,Data}, St) -> luerl_heap:alloc_userdata(Data, St); %% % Table refs should not be re-encoded %% encode(#tref{}=T, St) -> %% case luerl_heap:chk_table(T, St) of %% ok -> {T, St}; %% error -> error(badarg) %% end; encode(Term, _) -> error({badarg,Term}). %Can't encode anything else %% decode_list([LuerlTerm], State) -> [Term]. %% decode(LuerlTerm, State) -> Term. ?DOC( """ Decode a list of Luerl terms into their standard Erlang representation. Note that we have to detect recursive references and generate an error when this occurs. """). ?DOC( #{ group => <<"Encode/Decode Data functions">>} ). -spec decode_list([LuerlTerm], LuaState) -> [Term] when LuerlTerm :: luerldata(), Term :: term(), LuaState :: luerlstate(). decode_list(LuerlTerms, LuaState) -> lists:map(fun (Lt) -> decode(Lt, LuaState) end, LuerlTerms). ?DOC( """ Decode a Luerl term into its standard Erlang representation. Note that we have to detect recursive references and generate an error when this occurs. """). ?DOC( #{ group => <<"Encode/Decode Data functions">>} ). -spec decode(LuerlTerm, LuaState) -> Term when LuerlTerm :: luerldata(), Term :: term(), LuaState :: luerlstate(). decode(LT, St) -> decode(LT, St, []). decode(nil, _, _) -> nil; decode(false, _, _) -> false; decode(true, _, _) -> true; decode(B, _, _) when is_binary(B) -> B; decode(N, _, _) when is_number(N) -> N; %Integers and floats decode(#tref{}=T, St, In) -> decode_table(T, St, In); decode(#usdref{}=U, St, In) -> decode_userdata(U, St, In); decode(#funref{}=Fun, St, In) -> decode_luafunc(Fun, St, In); decode(#erl_func{}=Fun, St, In) -> decode_erlfunc(Fun, St, In); decode(#erl_mfa{}=Mfa, St, In) -> decode_erlmfa(Mfa, St, In); decode(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else decode_table(#tref{i=N}=T, St, In0) -> case lists:member(N, In0) of true -> error({recursive_table,T}); %Been here before false -> In1 = [N|In0], %We are in this as well case luerl_heap:get_table(T, St) of #table{a=Arr,d=Dict} -> Fun = fun (K, V, Acc) -> [{decode(K, St, In1),decode(V, St, In1)}|Acc] end, Ts = ttdict:fold(Fun, [], Dict), array:sparse_foldr(Fun, Ts, Arr); _Undefined -> error(badarg) end end. decode_userdata(U, St, _In) -> {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), {userdata,Data}. decode_luafunc(Fun, _St, _In) -> %% io:format("dec ~p\n", [Fun]), fun(Args, State) -> luerl_emul:functioncall(Fun, Args, State) end. decode_erlfunc(#erl_func{code=Fun}=_Ef, _St, _In) -> %% io:format("dec ~p\n", [Ef]), Fun. %Just the bare fun decode_erlmfa(#erl_mfa{m=Mod,f=Func,a=Arg}=_Mfa, _St, _In) -> %% io:format("mfa ~p\n", [Mfa]), {Mod,Func,Arg}. %% Externalize and Internalize ensure that the VM state passed in %% can be stored externally or can be recreated from external storage. %% Currently very simple: only random state needs special treatment. -spec externalize(LuaState) -> LuaState when LuaState :: luerlstate(). externalize(S) -> luerl_lib_math:externalize(S). -spec internalize(LuaState) -> LuaState when LuaState :: luerlstate(). internalize(S) -> luerl_lib_math:internalize(S). %% put_private(Key, Value, State) -> %% State. %% get_private(Key, State) -> %% Value. %% delete_private(Key, State) -> %% Value. ?DOC( """ Puts a private `Value` under `Key` that is not exposed to the runtime. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec put_private(Key, Value, LuaState) -> LuaState when Key :: term(), Value :: term(), LuaState :: luerlstate(). put_private(Key, Value, St) -> Private = maps:put(Key, Value, St#luerl.private), St#luerl{private=Private}. ?DOC( """ Get the private value for `Key`. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec get_private(Key, LuaState) -> Value when Key :: term(), Value :: term(), LuaState :: luerlstate(). get_private(Key, St) -> maps:get(Key, St#luerl.private). ?DOC( """ Delete the private value for `Key`. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec delete_private(Key, LuaState) -> LuaState when Key :: term(), LuaState :: luerlstate(). delete_private(Key, St) -> Private = maps:remove(Key, St#luerl.private), St#luerl{private=Private}. rvirding-luerl-772b25e/src/luerl_old.erl0000664000232200023220000003421315114130670020643 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_old.erl %% Authors : Robert Virding, Henning Diedrich %% Purpose : The original basic LUA 5.2 interface. -module(luerl_old). -include("luerl.hrl"). ?MODULEDOC(""" Legacy API for Luerl. This module is provided for backwards compatibility with earlier versions of Luerl. New code should use the luerl module instead. For Elixir users, the `Elixir.Luerl.Old` module provides an idiomatic interface with state as the first argument for better pipe operator usage. """). -export([eval/2,evalfile/2, do/2,dofile/2, load/2,load/3, loadfile/2,loadfile/3, path_loadfile/2,path_loadfile/3,path_loadfile/4, load_module/3,load_module1/3, call/3,call_chunk/3, call_function/3,call_function1/3,function_list/2, call_method/3,call_method1/3,method_list/2, get_table/2,get_table1/2,set_table/3,set_table1/3,set_table1/4, init/0,stop/1,gc/1, set_trace_func/2,clear_trace_func/1, set_trace_data/2,get_trace_data/1, get_stacktrace/1, externalize/1,internalize/1 ]). %% Encoding and decoding. -export([encode/2,encode_list/2,decode/2,decode_list/2]). %% luerl_old:eval(String|Binary|Form, State) -> Result. eval(Chunk, St0) -> try do(Chunk, St0) of {Ret,St1} -> {ok, decode_list(Ret, St1)} catch ?CATCH(_C, E, S) {error, E, S} %{error, {E, R}} ? <- todo: decide end. %% luerl_old:evalfile(Path, State) -> {ok, Result} | {error,Reason}. evalfile(Path, St0) -> try dofile(Path, St0) of {Ret,St1} -> {ok, decode_list(Ret, St1)} catch ?CATCH(_C, E, S) {error, E, S} %{error, {E, R}} ? <- todo: decide end. %% luerl_old:do(String|Binary|Form, State) -> {Result, NewState} do(S, St0) when is_binary(S); is_list(S) -> {ok,Func,St1} = load(S, St0), luerl_emul:call(Func, St1); do(Func, St) -> luerl_emul:call(Func, St). %% luerl_old:dofile(Path, State) -> {Result, NewState}. dofile(Path, St0) -> {ok,Func,St1} = loadfile(Path, St0), luerl_emul:call(Func, St1). %% load(String|Binary, State) -> {ok,Function,NewState}. %% load(String|Binary, Options, State) -> {ok,Function,NewState}. load(Bin, St) -> load(Bin, [return], St). load(Bin, Opts, St) when is_binary(Bin) -> load(binary_to_list(Bin), Opts, St); load(Str, Opts, St0) when is_list(Str) -> case luerl_comp:string(Str, Opts) of {ok,Chunk} -> {Func,St1} = luerl_emul:load_chunk(Chunk, St0), {ok,Func,St1}; {error,_,_}=E -> E end. %% loadfile(FileName, State) -> {ok,Function,NewState}. %% loadfile(FileName, Options, State) -> {ok,Function,NewState}. loadfile(Name, St) -> loadfile(Name, [return], St). loadfile(Name, Opts, St0) -> case luerl_comp:file(Name, Opts) of {ok,Chunk} -> {Func,St1} = luerl_emul:load_chunk(Chunk, St0), {ok,Func,St1}; {error,_,_}=E -> E end. %% path_loadfile(FileName, State) -> {ok,Function,FullName,State}. %% path_loadfile(Path, FileName, State) -> {ok,Function,FullName,State}. %% path_loadfile(Path, FileName, Options, State) -> %% {ok,Function,FullName,State}. %% When no path is given we use the value of LUA_LOAD_PATH. %% We manually step down the path to get the correct handling of %% filenames by the compiler. path_loadfile(Name, St) -> Path = case os:getenv("LUA_LOAD_PATH") of false -> []; %You get what you asked for Env -> %% Get path separator depending on os type. Sep = case os:type() of {win32,_} -> ";"; _ -> ":" %Unix end, string:tokens(Env, Sep) %Split into path list end, path_loadfile(Path, Name, [return], St). path_loadfile(Dirs, Name, St) -> path_loadfile(Dirs, Name, [return], St). path_loadfile([Dir|Dirs], Name, Opts, St0) -> Full = filename:join(Dir, Name), case loadfile(Full, Opts, St0) of {ok,Func,St1} -> {ok,Func,Full,St1}; {error,[{_,_,enoent}],_} -> %Couldn't find the file path_loadfile(Dirs, Name, St0); Error -> Error end; path_loadfile([], _, _, _) -> {error,[{none,file,enoent}],[]}. %% load_module(TablePath, ModuleName, State) -> State. %% load_module1(LuaTablePath, ModuleName, State) -> State. %% Load module and add module table to the path. load_module(Fp, Mod, St0) when is_list(Fp) -> {Lfp,St1} = encode_list(Fp, St0), load_module1(Lfp, Mod, St1); load_module(_, _,_) -> error(badarg). load_module1(Lfp, Mod, St0) -> {Tab,St1} = Mod:install(St0), luerl_emul:set_table_keys(Lfp, Tab, St1). %% init() -> State. init() -> luerl_emul:init(). %% call(Chunk, Args, State) -> {Result,State} call(C, As, St) -> call_chunk(C, As, St). call_chunk(C, As, St0) -> {Las,St1} = encode_list(As, St0), {Lrs,St2} = luerl_emul:call(C, Las, St1), Rs = decode_list(Lrs, St2), {Rs,St2}. %% call_function(TablePath, Args, State) -> {Result,State}. %% call_function1(LuaTablePath | Func, LuaArgs, State) -> {LuaResult,State}. call_function(Fp, As, St0) -> %% Encode the input arguments. {Lfp,St1} = encode_list(Fp, St0), {Las,St2} = encode_list(As, St1), %% Find the function definition and call function. {Lrs,St3} = call_function1(Lfp, Las, St2), Rs = decode_list(Lrs, St3), {Rs,St3}. call_function1(Lfp, Las, St0) when is_list(Lfp) -> {F,St1} = luerl_emul:get_table_keys(Lfp, St0), luerl_emul:functioncall(F, Las, St1); call_function1(F, Las, St) -> luerl_emul:functioncall(F, Las, St). %% function_list(Keys, State) -> {V,State}. %% Go down a list of keys and return final value. function_list(Ks, St) -> luerl_emul:get_table_keys(Ks, St). %% call_method(FuncPath, Args, State) -> {Result,State}. %% call_method1(FuncPath | FuncPath, Args, State) -> {Result,State}. call_method(Fp, As, St0) -> %% Encode the input arguments. {Lfp,St1} = encode_list(Fp, St0), {Las,St2} = encode_list(As, St1), %% Find the object and method definition and call method. {O,M,St3} = method_list(Lfp, St2), {Lrs,St4} = luerl_emul:functioncall(M, [O|Las], St3), Rs = decode_list(Lrs, St4), {Rs,St4}. call_method1(Fp, Las, St0) -> %% Find the object and method definition and call method. {O,M,St1} = method_list(Fp, St0), luerl_emul:functioncall(M, [O|Las], St1). method_list([G|Ks], St0) -> {First,St1} = luerl_emul:get_global_key(G, St0), method_list(First, Ks, St1). method_list(Tab, [K], St0) -> {Func,St1} = luerl_emul:get_table_key(Tab, K, St0), {Tab,Func,St1}; method_list(Tab, [K|Ks], St0) -> {Next,St1} = luerl_emul:get_table_key(Tab, K, St0), method_list(Next, Ks, St1); method_list(_, _, _) -> error(badarg). %% get_table(TablePath, State) -> {Result, State}. %% Go down a list of keys and return decoded final value. get_table(Fp, St0) when is_list(Fp) -> {Lfp,St1} = encode_list(Fp, St0), {V,St2} = luerl_emul:get_table_keys(Lfp, St1), Vd = decode(V, St2), {Vd,St2}; get_table(_,_) -> error(badarg). %% get_table1(LuaTablePath, State) -> {LuaResult, State}. get_table1(Fp, St) when is_list(Fp) -> luerl_emul:get_table_keys(Fp, St); get_table1(_,_) -> error(badarg). %% set_table(TablePath, Value, State) -> State. %% Go down a list of keys and set final key to Value. set_table(Fp, V, St0) when is_list(Fp) -> {Lfp,St1} = encode_list(Fp, St0), {Lv, St2} = encode(V, St1), set_table1(Lfp, Lv, St2); set_table(_,_,_) -> error(badarg). %% set_table1(LuaTablePath, Value, State) -> State. %% Must explicitly read table key to get set_table1(Lfp, Lv, St) -> luerl_emul:set_table_keys(Lfp, Lv, St). %% set_table1(Table, Key, Value, State) -> State. %% Must explicitly read table key to get set_table1(Tab, Key, Lv, St) -> luerl_emul:set_table_key(Tab, Key, Lv, St). %% stop(State) -> GCedState. stop(St) -> luerl_heap:gc(St). %% gc(State) -> State. gc(St) -> luerl_heap:gc(St). %% set_trace_func(TraceFunction, State) -> State. %% clear_trace_func(State) -> State. %% get_trace_data(State) -> TraceData. %% set_trace_data(TraceData, State) -> State. %% Set the trace function and access the trace data. set_trace_func(Tfunc, St) -> St#luerl{trace_func=Tfunc}. clear_trace_func(St) -> St#luerl{trace_func=none}. get_trace_data(St) -> St#luerl.trace_data. set_trace_data(Tdata, St) -> St#luerl{trace_data=Tdata}. %% get_stacktrace(State) -> [{FuncName,[{file,FileName},{line,Line}]}]. get_stacktrace(#luerl{cs=Stack}=St) -> Fun = fun (Frame, Acc) -> do_stackframe(Frame, Acc, St) end, {_,Trace} = lists:foldl(Fun, {1,[]}, Stack), lists:reverse(Trace). do_stackframe(#call_frame{func=Funref,args=Args}, {Line,Trace}, St) -> case Funref of #funref{} -> {Func,_} = luerl_heap:get_funcdef(Funref, St), Anno = Func#lua_func.anno, Name = case luerl_anno:get(name, Anno) of undefined -> <<"-no-name-">>; N -> N end, File = luerl_anno:get(file, Anno), {Line,[{Name,Args,[{file,File},{line,Line}]} | Trace]}; #erl_func{code=Fun} -> {module,Module} = erlang:fun_info(Fun, module), {name,Name} = erlang:fun_info(Fun, name), FileName = get_filename(Module), {Line,[{{Module,Name},Args,[{file,FileName}]} | Trace]}; #erl_mfa{m=M,f=F,a=A} -> FileName = get_filename(M), %% {Line,[{{M,F},{A,Args},[{file,FileName}]} | Trace]}; %% {Line,[{{M,F},[A | Args],[{file,FileName}]} | Trace]}; {Line,[{{M,F,A},Args,[{file,FileName}]} | Trace]}; Other -> {Line,[{Other,Args,[{file,<<"-no-file-">>},{line,Line}]} | Trace]} end; do_stackframe(#current_line{line=Line}, {_,Trace}, _St) -> {Line,Trace}; do_stackframe(#loop_frame{}, Acc, _St) -> %Ignore these Acc. get_filename(Mod) -> Comp = erlang:get_module_info(Mod, compile), case lists:keyfind(source, 1, Comp) of {source,FileName} -> BaseName = filename:basename(FileName), list_to_binary(BaseName); false -> %The compiler doesn't know <<"-no-file-">> end. %% Define IS_MAP/1 macro for is_map/1 bif. -ifdef(HAS_MAPS). -define(IS_MAP(T), is_map(T)). -else. -define(IS_MAP(T), false). -endif. %% encode_list([Term], State) -> {[LuerlTerm],State}. %% encode(Term, State) -> {LuerlTerm,State}. encode_list(Ts, St) -> lists:mapfoldl(fun encode/2, St, Ts). encode(nil, St) -> {nil,St}; encode(false, St) -> {false,St}; encode(true, St) -> {true,St}; encode(B, St) when is_binary(B) -> {B,St}; encode(A, St) when is_atom(A) -> {atom_to_binary(A, utf8),St}; encode(N, St) when is_number(N) -> {N,St}; %Integers and floats encode(F, St) when ?IS_MAP(F) -> encode(maps:to_list(F), St); encode(L, St0) when is_list(L) -> {Es,{_,St1}} = lists:mapfoldl(fun ({K0,V0}, {I,S0}) -> {K1,S1} = encode(K0, S0), {V1,S2} = encode(V0, S1), {{K1,V1},{I,S2}}; (V0, {I,S0}) -> {V1,S1} = encode(V0, S0), {{I,V1},{I+1,S1}} end, {1,St0}, L), {T,St2} = luerl_heap:alloc_table(Es, St1), {T,St2}; %No more to do for now encode(F, St) when is_function(F, 2) -> F1 = fun(Args, State) -> Args1 = decode_list(Args, State), {Res, State1} = F(Args1, State), encode_list(Res, State1) end, {#erl_func{code=F1}, St}; encode(F, St) when is_function(F, 1) -> F1 = fun(Args, State) -> Args1 = decode_list(Args, State), Res = F(Args1), encode_list(Res, State) end, {#erl_func{code=F1}, St}; encode({M,F,A}, St) when is_atom(M) and is_atom(F) -> {#erl_mfa{m=M,f=F,a=A}, St}; encode({userdata,Data}, St) -> luerl_heap:alloc_userdata(Data, St); % Table refs should not be re-encoded encode(#tref{}=T, St) -> case luerl_heap:chk_table(T, St) of ok -> {T, St}; error -> error(badarg) end; encode(Term, _) -> error({badarg,Term}). %Can't encode anything else %% decode_list([LuerlTerm], State) -> [Term]. %% decode(LuerlTerm, State) -> Term. %% In decode we track of which tables we have seen to detect %% recursive references and generate an error when that occurs. decode_list(Lts, St) -> lists:map(fun (Lt) -> decode(Lt, St) end, Lts). decode(LT, St) -> decode(LT, St, []). decode(nil, _, _) -> nil; decode(false, _, _) -> false; decode(true, _, _) -> true; decode(B, _, _) when is_binary(B) -> B; decode(N, _, _) when is_number(N) -> N; %Integers and floats decode(#tref{}=T, St, In) -> decode_table(T, St, In); decode(#usdref{}=U, St, _) -> decode_userdata(U, St); decode(#funref{}=Fun, State, _) -> F = fun(Args) -> {Args1, State1} = encode_list(Args, State), {Ret, State2} = luerl_emul:functioncall(Fun, Args1, State1), decode_list(Ret, State2) end, F; %Just a bare fun decode(#erl_func{code=Fun}, _, _) -> Fun; decode(#erl_mfa{m=M,f=F,a=A}, _, _) -> {M,F,A}; decode(Lua, _, _) -> error({badarg,Lua}). %Shouldn't have anything else decode_table(#tref{i=N}=T, St, In0) -> case lists:member(N, In0) of true -> error({recursive_table,T}); %Been here before false -> In1 = [N|In0], %We are in this as well case luerl_heap:get_table(T, St) of #table{a=Arr,d=Dict} -> Fun = fun (K, V, Acc) -> [{decode(K, St, In1),decode(V, St, In1)}|Acc] end, Ts = ttdict:fold(Fun, [], Dict), array:sparse_foldr(Fun, Ts, Arr); _Undefined -> error(badarg) end end. decode_userdata(U, St) -> {#userdata{d=Data},_} = luerl_heap:get_userdata(U, St), {userdata,Data}. %% Externalize and Internalize ensure that the VM state passed in %% can be stored externally or can be recreated from external storage. %% Currently very simple: only random state needs special treatment. externalize(S) -> luerl_lib_math:externalize(S). internalize(S) -> luerl_lib_math:internalize(S). rvirding-luerl-772b25e/src/luerl_app.erl0000664000232200023220000000175015114130670020645 0ustar debalancedebalance%% Copyright (c) 2013-2021 Robert Virding %% %% 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. -module(luerl_app). -include("luerl.hrl"). ?MODULEDOC(false). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %% =================================================================== %% Application callbacks %% =================================================================== start(_StartType, _StartArgs) -> luerl_sup:start_link(). stop(_State) -> ok. rvirding-luerl-772b25e/src/luerl_emul.erl0000664000232200023220000014343515114130670021036 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_emul.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 machine emulator. %% First version of emulator. Compiler so far only explicitly handles %% local/global variables. %% %% We explicitly mirror the parser rules which generate the AST and do %% not try to fold similar structures into common code. While this %% means we get more code it also becomes more explicit and clear what %% we are doing. It may also allow for specific optimisations. And %% example is that we DON'T fold 'var' and 'funcname' even though they %% are almost the same. %% %% Issues: how should we handle '...'? Now we treat it as any (local) %% variable. -module(luerl_emul). -include("luerl.hrl"). -include("luerl_comp.hrl"). -include("luerl_instrs.hrl"). ?MODULEDOC(false). %% Basic interface. -export([init/0,gc/1]). -export([call/2,call/3,emul/2]). -export([load_chunk/2,load_chunk/3]). -export([functioncall/3,methodcall/4, set_global_key/3,get_global_key/2, get_table_keys/2,get_table_keys/3, set_table_keys/3,set_table_keys/4, get_table_key/3,set_table_key/4 ]). %% Temporary shadow calls. -export([alloc_table/2,set_userdata/3,get_metamethod/3]). %% For testing. -export([pop_vals/2,push_vals/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %% -compile(inline). %For when we are optimising %% -compile({inline,[boolean_value/1,first_value/1]}). %% -define(ITRACE_DO(Expr), ok). -define(ITRACE_DO(Expr), begin (get(luerl_itrace) /= undefined) andalso Expr end). %% Temporary shadow calls. gc(St) -> luerl_heap:gc(St). alloc_table(Itab, St) -> luerl_heap:alloc_table(Itab, St). set_userdata(Ref, Data, St) -> luerl_heap:set_userdata(Ref, Data, St). get_metamethod(Obj, Event, St) -> luerl_heap:get_metamethod(Obj, Event, St). %% init() -> State. %% Initialise the basic state. init() -> St1 = luerl_heap:init(), %% Allocate the _G table and initialise the environment {_G,St2} = luerl_lib_basic:install(St1), %Global environment St3 = St2#luerl{g=_G}, %% Now we can start adding libraries. Package MUST be first! St4 = load_lib(<<"package">>, luerl_lib_package, St3), %% Add the other standard libraries. St5 = load_libs([ {<<"bit32">>,luerl_lib_bit32}, {<<"io">>,luerl_lib_io}, {<<"math">>,luerl_lib_math}, {<<"os">>,luerl_lib_os}, {<<"string">>,luerl_lib_string}, {<<"utf8">>,luerl_lib_utf8}, {<<"table">>,luerl_lib_table}, {<<"debug">>,luerl_lib_debug} ], St4), %% Set _G variable to point to it and add it to packages.loaded. St6 = set_global_key(<<"_G">>, _G, St5), set_table_keys([<<"package">>,<<"loaded">>,<<"_G">>], _G, St6). load_libs(Libs, St) -> Fun = fun ({Key,Mod}, S) -> load_lib(Key, Mod, S) end, lists:foldl(Fun, St, Libs). %% load_lib(Key, Module, State) -> State. load_lib(Key, Mod, St0) -> {Tab,St1} = Mod:install(St0), %% Add key to global and to package.loaded. St2 = set_global_key(Key, Tab, St1), set_table_keys([<<"package">>,<<"loaded">>,Key], Tab, St2). %% set_global_key(Key, Value, State) -> State. %% get_global_key(Key, State) -> {[Val],State}. %% Access elements in the global name table, _G. set_global_key(Key, Val, #luerl{g=G}=St) -> set_table_key(G, Key, Val, St). get_global_key(Key, #luerl{g=G}=St) -> get_table_key(G, Key, St). %% get_table_keys(Keys, State) -> {Value,State}. %% get_table_keys(Tab, Keys, State) -> {Value,State}. %% Search down tables which stops when no more tables. get_table_keys(Keys, St) -> get_table_keys(St#luerl.g, Keys, St). get_table_keys(Tab, [K|Ks], St0) -> {Val,St1} = get_table_key(Tab, K, St0), get_table_keys(Val, Ks, St1); get_table_keys(Val, [], St) -> {Val,St}. %% set_table_keys(Keys, Val, State) -> State. %% set_table_keys(Tab, Keys, Val, State) -> State. %% Setter down tables. set_table_keys(Keys, Val, St) -> set_table_keys(St#luerl.g, Keys, Val, St). set_table_keys(Tab, [K], Val, St) -> set_table_key(Tab, K, Val, St); set_table_keys(Tab0, [K|Ks], Val, St0) -> {Tab1,St1} = get_table_key(Tab0, K, St0), set_table_keys(Tab1, Ks, Val, St1). %% set_table_key(Tref, Key, Value, State) -> State. %% get_table_key(Tref, Key, State) -> {Val,State}. %% Access tables, as opposed to the environment (which are also %% tables). Setting a value to 'nil' will clear it from the array but %% not from the table; however, we won't add a nil value. %% NOTE: WE ALWAYS RETURN A SINGLE VALUE! set_table_key(Tref, Key, Val, St0) -> case luerl_heap:set_table_key(Tref, Key, Val, St0) of {value,_Val,St1} -> St1; {meta,Meth,Args,St1} -> {_Ret,St2} = functioncall(Meth, Args, St1), St2; {error,Error,St1} -> lua_error(Error, St1) end. get_table_key(Tref, Key, St0) -> case luerl_heap:get_table_key(Tref, Key, St0) of {value,Val,St1} -> {Val,St1}; {meta,Meth,Args,St1} -> {Ret,St2} = functioncall(Meth, Args, St1), {first_value(Ret),St2}; {error,Error,St1} -> lua_error(Error, St1) end. %% set_local_var(Depth, Index, Var, Frames) -> Frames. %% get_local_var(Depth, Index, Frames) -> Val. set_local_var(1, I, V, [F|Fs]) -> [setelement(I, F, V)|Fs]; set_local_var(D, I, V, [F|Fs]) -> [F|set_local_var(D-1, I, V, Fs)]. get_local_var(1, I, [F|_]) -> element(I, F); get_local_var(D, I, [_|Fs]) -> get_local_var(D-1, I, Fs). %% set_env_var(Depth, Index, Val, EnvStack, State) -> State. %% get_env_var(Depth, Index, EnvStack, State) -> Val. %% We must have the state as the environments are global in the %% state. set_env_var(D, I, Val, Estk, St) -> St1 = set_env_var_1(D, I, Val, Estk, St), %% io:format("******** SEV DONE ~w ~w ********\n", [D,I]), St1. set_env_var_1(1, I, Val, [Eref|_], St) -> luerl_heap:set_env_var(Eref, I, Val, St); set_env_var_1(2, I, Val, [_,Eref|_], St) -> luerl_heap:set_env_var(Eref, I, Val, St); set_env_var_1(D, I, Val, Env, St) -> luerl_heap:set_env_var(lists:nth(D, Env), I, Val, St). get_env_var(D, I, Env, St) -> Val = get_env_var_1(D, I, Env, St), %% io:format("******** GEV DONE ~w ~w ********\n", [D, I]), Val. get_env_var_1(1, I, [Eref|_], St) -> luerl_heap:get_env_var(Eref, I, St); get_env_var_1(2, I, [_,Eref|_], St) -> luerl_heap:get_env_var(Eref, I, St); get_env_var_1(D, I, Env, St) -> luerl_heap:get_env_var(lists:nth(D, Env), I, St). %% load_chunk(FunctionDefCode, State) -> {Function,State}. %% load_chunk(FunctionDefCode, Env, State) -> {Function,State}. %% Load a chunk from the compiler which is a compiled function %% definition whose instructions define everything. Return a callable %% function reference which defines everything and a updated Luerl %% state. load_chunk(Code, St) -> load_chunk(Code, [], St). load_chunk([Code], [], St0) -> {?PUSH_FDEF(Funref),_,St1} = load_chunk_i(Code, [], St0), {Funref,St1}. %% load_chunk_i(Instr, FuncRefs, Status) -> {Instr,FuncRefs,State}. %% load_chunk_is(Instrs, FuncRefs, Status) -> {Instrs,FuncRefs,State}. %% Load chunk instructions. We keep track of the functions refs and %% save the ones directly accessed in each function. This will make %% gc easier as we will not have to step through the function code at %% gc time. load_chunk_is([I0|Is0], Funrs0, St0) -> {I1,Funrs1,St1} = load_chunk_i(I0, Funrs0, St0), {Is1,Funrs2,St2} = load_chunk_is(Is0, Funrs1, St1), {[I1|Is1],Funrs2,St2}; load_chunk_is([], Funrs, St) -> {[],Funrs,St}. %% First the instructions with nested code. %% We include the dymanmic instructions here even though the compiler %% does not generate them. This should make us more future proof. load_chunk_i(?PUSH_FDEF(Anno, Lsz, Esz, Pars, B0), Funrs0, St0) -> {B1,Funrs,St1} = load_chunk_is(B0, [], St0), Fdef = #lua_func{anno=Anno,funrefs=Funrs,lsz=Lsz,esz=Esz,pars=Pars,body=B1}, {Funref,St2} = luerl_heap:alloc_funcdef(Fdef, St1), Funrs1 = ordsets:add_element(Funref, Funrs0), {?PUSH_FDEF(Funref),Funrs1,St2}; load_chunk_i(?BLOCK(Lsz, Esz, B0), Funrs0, St0) -> {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?BLOCK(Lsz, Esz, B1),Funrs1,St1}; load_chunk_i(?REPEAT(B0), Funrs0, St0) -> {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?REPEAT(B1),Funrs1,St1}; load_chunk_i(?REPEAT_LOOP(B0), Funrs0, St0) -> %This is dynamic {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?REPEAT_LOOP(B1),Funrs1,St1}; load_chunk_i(?WHILE(E0, B0), Funrs0, St0) -> {E1,Funrs1,St1} = load_chunk_is(E0, Funrs0, St0), {B1,Funrs2,St2} = load_chunk_is(B0, Funrs1, St1), {?WHILE(E1, B1),Funrs2,St2}; load_chunk_i(?WHILE_LOOP(E0, B0), Funrs0, St0) -> {E1,Funrs1,St1} = load_chunk_is(E0, Funrs0, St0), {B1,Funrs2,St2} = load_chunk_is(B0, Funrs1, St1), {?WHILE_LOOP(E1, B1),Funrs2,St2}; load_chunk_i(?AND_THEN(T0), Funrs0, St0) -> {T1,Funrs1,St1} = load_chunk_is(T0, Funrs0, St0), {?AND_THEN(T1),Funrs1,St1}; load_chunk_i(?OR_ELSE(T0), Funrs0, St0) -> {T1,Funrs1,St1} = load_chunk_is(T0, Funrs0, St0), {?OR_ELSE(T1),Funrs1,St1}; load_chunk_i(?IF_TRUE(T0), Funrs0, St0) -> {T1,Funrs1,St1} = load_chunk_is(T0, Funrs0, St0), {?IF_TRUE(T1),Funrs1,St1}; load_chunk_i(?IF(T0, F0), Funrs0, St0) -> {T1,Funrs1,St1} = load_chunk_is(T0, Funrs0, St0), {F1,Funrs2,St2} = load_chunk_is(F0, Funrs1, St1), {?IF(T1, F1),Funrs2,St2}; load_chunk_i(?NFOR(V, B0), Funrs0, St0) -> {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?NFOR(V, B1),Funrs1,St1}; load_chunk_i(?NFOR_LOOP(N, L, S, B0), Funrs0, St0) -> %This is dynamic {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?NFOR_LOOP(N, L, S, B1),Funrs1,St1}; load_chunk_i(?GFOR(Vs, B0), Funrs0, St0) -> {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?GFOR(Vs, B1),Funrs1,St1}; load_chunk_i(?GFOR_CALL(F, D, V, B0), Funrs0, St0) -> %This is dynamic {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?GFOR_CALL(F, D, V, B1),Funrs1,St1}; load_chunk_i(?GFOR_LOOP(F, D, B0), Funrs0, St0) -> %This is dynamic {B1,Funrs1,St1} = load_chunk_is(B0, Funrs0, St0), {?GFOR_LOOP(F, D, B1),Funrs1,St1}; %% Then the rest which we don't have to worry about. load_chunk_i(I, Funrs, St) -> {I,Funrs,St}. %% call(Function, State) -> {Return,State}. %% call(Function, Args, State) -> {Return,State}. %% functioncall(Function, Args, State) -> {Return,State}. %% methodcall(Object, Method, Args, State) -> {Return,State}. %% These ares called from the outside and expect everything necessary %% to be in the state. call(Func, St) -> call(Func, [], St). call(#funref{}=Funref, Args, St0) -> %Lua function {Ret,St1} = functioncall(Funref, Args, St0), %% Should do GC here. {Ret,St1}; call(#erl_func{}=Func, Args, St0) -> %Erlang function {Ret,St1} = functioncall(Func, Args, St0), %% Should do GC here. {Ret,St1}; call(#erl_mfa{}=Func, Args, St0) -> %Erlang function as MFA triplet {Ret,St1} = functioncall(Func, Args, St0), {Ret,St1}. functioncall(Func, Args, #luerl{stk=Stk}=St0) -> Fr = #call_frame{func=Func,args=Args,lvs=[],env=[],is=[],cont=[]}, Cs0 = [Fr], {_Lvs,[Ret|_],_Env,Cs1,St1} = call_function(Func, Args, Stk, Cs0, St0), {Ret,St1#luerl{stk=Stk,cs=Cs1}}. %Reset the stacks methodcall(Obj, Meth, Args, St0) -> %% Get the function to call from object and method. case get_table_key(Obj, Meth, St0) of {nil,St1} -> %No method lua_error({undefined_method,Obj,Meth}, St1); {Func,St1} -> functioncall(Func, [Obj|Args], St1) end. %% emul(Instrs, State). %% emul(Instrs, Continuation, LocalVariables, Stack, Env, CallStack, State). %% The cost of checking the itrace process variable is very slight %% compared to everything else. emul(Is, St) -> emul(Is, [], {}, [], [], [], St). %% The faster (yeah sure) version. %% emul(Is, Cont, Lvs, Stk, Env, Cs, St) -> %% emul_1(Is, Cont, Lvs, Stk, Env, Cs, St). %% The tracing versions. emul([I|_]=Is, Cont, Lvs, Stk, Env, Cs, St) -> ?ITRACE_DO(begin io:fwrite("Is: ~p\n", [Is]), io:fwrite("Cnt: ~p\n", [Cont]), io:fwrite("Lvs: ~p\n", [Lvs]), io:fwrite("Env: ~p\n", [Env]), io:fwrite("Stk: ~p\n", [Stk]), io:fwrite("Cs: ~p\n", [Cs]), io:fwrite("I: ~p\n", [I]), io:put_chars("--------\n") end), emul_1(Is, Cont, Lvs, Stk, Env, Cs, St); emul([], Cont, Lvs, Stk, Env, Cs, St) -> ?ITRACE_DO(begin io:fwrite("Is: ~p\n", [[]]), io:fwrite("Cnt: ~p\n", [Cont]), io:fwrite("Lvs: ~p\n", [Lvs]), io:fwrite("Env: ~p\n", [Env]), io:fwrite("Stk: ~p\n", [Stk]), io:fwrite("Cs: ~p\n", [Cs]), io:put_chars("--------\n") end), emul_1([], Cont, Lvs, Stk, Env, Cs, St). %% itrace_print(Format, Args) -> %% ?ITRACE_DO(io:fwrite(Format, Args)). %% Expression instructions. emul_1([?PUSH_LIT(L)|Is], Cont, Lvs, Stk, Env, Cs, St) -> emul(Is, Cont, Lvs, [L|Stk], Env, Cs, St); emul_1([?PUSH_LVAR(D, I)|Is], Cont, Lvs, Stk, Env, Cs, St) -> Val = get_local_var(D, I, Lvs), emul(Is, Cont, Lvs, [Val|Stk], Env, Cs, St); emul_1([?PUSH_EVAR(D, I)|Is], Cont, Lvs, Stk, Env, Cs, St) -> Val = get_env_var(D, I, Env, St), emul(Is, Cont, Lvs, [Val|Stk], Env, Cs, St); emul_1([?PUSH_GVAR(Key)|Is], Cont, Lvs, Stk, Env, Cs, St0) -> %% We must handle the metamethod and error here. case luerl_heap:get_global_key(Key, St0) of {value,Val,St1} -> emul(Is, Cont, Lvs, [Val|Stk], Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?SINGLE|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end; emul_1([?PUSH_LAST_LIT(L)|Is], Cont, Lvs, Stk, Env, Cs, St) -> emul(Is, Cont, Lvs, [[L]|Stk], Env, Cs, St); emul_1([?PUSH_LAST_LVAR(D, I)|Is], Cont, Lvs, Stk, Env, Cs, St) -> Val = get_local_var(D, I, Lvs), emul(Is, Cont, Lvs, [[Val]|Stk], Env, Cs, St); emul_1([?PUSH_LAST_EVAR(D, I)|Is], Cont, Lvs, Stk, Env, Cs, St) -> Val = get_env_var(D, I, Env, St), emul(Is, Cont, Lvs, [[Val]|Stk], Env, Cs, St); emul_1([?PUSH_LAST_GVAR(Key)|Is], Cont, Lvs, Stk, Env, Cs, St0) -> %% We must handle the metamethod and error here. case luerl_heap:get_global_key(Key, St0) of {value,Val,St1} -> emul(Is, Cont, Lvs, [[Val]|Stk], Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end; emul_1([?STORE_LVAR(D, I)|Is], Cont, Lvs0, [Val|Stk], Env, Cs, St) -> Lvs1 = set_local_var(D, I, Val, Lvs0), emul(Is, Cont, Lvs1, Stk, Env, Cs, St); emul_1([?STORE_EVAR(D, I)|Is], Cont, Lvs, [Val|Stk], Env, Cs, St0) -> St1 = set_env_var(D, I, Val, Env, St0), emul(Is, Cont, Lvs, Stk, Env, Cs, St1); emul_1([?STORE_GVAR(Key)|Is], Cont, Lvs, [Val|Stk], Env, Cs, St0) -> %% We must handle the metamethod and error here. case luerl_heap:set_global_key(Key, Val, St0) of {value,_,St1} -> emul(Is, Cont, Lvs, Stk, Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?POP|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end; emul_1([?GET_KEY|Is], Cont, Lvs, [Key,Tab|Stk], Env, Cs, St) -> do_get_key(Is, Cont, Lvs, Stk, Env, Cs, St, Tab, Key); emul_1([?GET_LIT_KEY(Key)|Is], Cont, Lvs, [Tab|Stk], Env, Cs, St) -> %% [?PUSH_LIT(Key),?GET_KEY] do_get_key(Is, Cont, Lvs, Stk, Env, Cs, St, Tab, Key); emul_1([?SET_KEY|Is], Cont, Lvs, [Key,Tab,Val|Stk], Env, Cs, St) -> do_set_key(Is, Cont, Lvs, Stk, Env, Cs, St, Tab, Key, Val); emul_1([?SET_LIT_KEY(Key)|Is], Cont, Lvs, [Tab,Val|Stk], Env, Cs, St) -> %% [?PUSH_LIT(Key),?SET_KEY] do_set_key(Is, Cont, Lvs, Stk, Env, Cs, St, Tab, Key, Val); emul_1([?SINGLE|Is], Cont, Lvs, [Val|Stk], Env, Cs, St) -> emul(Is, Cont, Lvs, [first_value(Val)|Stk], Env, Cs, St); emul_1([?MULTIPLE|Is], Cont, Lvs, [Val|Stk], Env, Cs, St) -> emul(Is, Cont, Lvs, [multiple_value(Val)|Stk], Env, Cs, St); emul_1([?BUILD_TAB(Fc, I)|Is], Cont, Lvs, Stk0, Env, Cs, St0) -> {Tab,Stk1,St1} = build_tab(Fc, I, Stk0, St0), emul(Is, Cont, Lvs, [Tab|Stk1], Env, Cs, St1); emul_1([?FCALL|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_fcall(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?TAIL_FCALL|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_tail_fcall(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?MCALL(M)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_mcall(Is, Cont, Lvs, Stk, Env, Cs, St, M); emul_1([?TAIL_MCALL(M)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_tail_mcall(Is, Cont, Lvs, Stk, Env, Cs, St, M); emul_1([?OP(Op,1)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_op1(Is, Cont, Lvs, Stk, Env, Cs, St, Op); emul_1([?OP(Op,2)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_op2(Is, Cont, Lvs, Stk, Env, Cs, St, Op); emul_1([?PUSH_FDEF(Funref)|Is], Cont, Lvs, Stk, Env, Cs, St0) -> %% Update the env field of the function reference with the current %% environment. Funref1 = Funref#funref{env=Env}, emul(Is, Cont, Lvs, [Funref1|Stk], Env, Cs, St0); %% Control instructions. emul_1([?BLOCK(Lsz, Esz, Bis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_block(Is, Cont, Lvs, Stk, Env, Cs, St, Lsz, Esz, Bis); emul_1([?BLOCK_OPEN(Lsz, Esz)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_block_open(Is, Cont, Lvs, Stk, Env, Cs, St, Lsz, Esz); emul_1([?BLOCK_CLOSE|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_block_close(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?WHILE(Eis, Wis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_while(Is, Cont, Lvs, Stk, Env, Cs, St, Eis, Wis); emul_1([?WHILE_LOOP(Eis, Wis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_while_loop(Is, Cont, Lvs, Stk, Env, Cs, St, Eis, Wis); emul_1([?REPEAT(Ris)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_repeat(Is, Cont, Lvs, Stk, Env, Cs, St, Ris); emul_1([?REPEAT_LOOP(Ris)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_repeat_loop(Is, Cont, Lvs, Stk, Env, Cs, St, Ris); emul_1([?AND_THEN(Then)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_and_then(Is, Cont, Lvs, Stk, Env, Cs, St, Then); emul_1([?OR_ELSE(Else)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_or_else(Is, Cont, Lvs, Stk, Env, Cs, St, Else); emul_1([?IF_TRUE(True)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_if_true(Is, Cont, Lvs, Stk, Env, Cs, St, True); emul_1([?IF(True, False)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_if(Is, Cont, Lvs, Stk, Env, Cs, St, True, False); emul_1([?NFOR(V, Fis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_numfor(Is, Cont, Lvs, Stk, Env, Cs, St, V, Fis); emul_1([?NFOR_LOOP(N,L,S,Fis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_numfor_loop(Is, Cont, Lvs, Stk, Env, Cs, St, N, L, S, Fis); emul_1([?GFOR(Vs, Fis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_genfor(Is, Cont, Lvs, Stk, Env, Cs, St, Vs, Fis); emul_1([?GFOR_CALL(Func, Data, Val, Fis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_genfor_call(Is, Cont, Lvs, Stk, Env, Cs, St, Func, Data, Val, Fis); emul_1([?GFOR_LOOP(Func, Data, Fis)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_genfor_loop(Is, Cont, Lvs, Stk, Env, Cs, St, Func, Data, Fis); emul_1([?BREAK|_], _Cont, Lvs, _Stk, _Env, Cs, St) -> do_break(Lvs, Cs, St); emul_1([?RETURN(Ac)|_], _Cont, _Lvs, Stk, _Env, Cs, St) -> do_return(Ac, Stk, Cs, St); %% Stack instructions emul_1([?POP|Is], Cont, Lvs, [_|Stk], Env, Cs, St) -> emul(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?POP2|Is], Cont, Lvs, [_,_|Stk], Env, Cs, St) -> emul(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?SWAP|Is], Cont, Lvs, [S1,S2|Stk], Env, Cs, St) -> emul(Is, Cont, Lvs, [S2,S1|Stk], Env, Cs, St); emul_1([?DUP|Is], Cont, Lvs, [V|_]=Stk, Env, Cs, St) -> emul(Is, Cont, Lvs, [V|Stk], Env, Cs, St); emul_1([?PUSH_VALS(Vc)|Is], Cont, Lvs, [Vals|Stk0], Env, Cs, St) -> %% Pop value list off the stack and push Vc vals from it. Stk1 = push_vals(Vc, Vals, Stk0), emul(Is, Cont, Lvs, Stk1, Env, Cs, St); emul_1([?POP_VALS(Vc)|Is], Cont, Lvs, Stk0, Env, Cs, St) -> %% Pop Vc vals off the stack, put in a list and push onto the stack. {Vals,Stk1} = pop_vals(Vc, Stk0), emul(Is, Cont, Lvs, [Vals|Stk1], Env, Cs, St); emul_1([?PUSH_ARGS(Al)|Is], Cont, Lvs, [Args|Stk0], Env, Cs, St) -> %% Pop argument list off the stack and push args onto the stack. Stk1 = push_args(Al, Args, Stk0), emul(Is, Cont, Lvs, Stk1, Env, Cs, St); emul_1([?POP_ARGS(Ac)|Is], Cont, Lvs, Stk0, Env, Cs, St) -> %% Pop Ac args off the stack, put in a list and push onto the stack. {Args,Stk1} = pop_vals(Ac, Stk0), emul(Is, Cont, Lvs, [Args|Stk1], Env, Cs, St); emul_1([?COMMENT(_)|Is], Cont, Lvs, Stk, Env, Cs, St) -> %% This just a comment which is ignored. emul(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([?CURRENT_LINE(Line,File)|Is], Cont, Lvs, Stk, Env, Cs, St) -> do_current_line(Is, Cont, Lvs, Stk, Env, Cs, St, Line, File); emul_1([], [Is|Cont], Lvs, Stk, Env, Cs, St) -> emul(Is, Cont, Lvs, Stk, Env, Cs, St); emul_1([], [], Lvs, Stk, Env, Cs, St) -> {Lvs,Stk,Env,Cs,St}. %% pop_vals(Count, Stack) -> {ValList,Stack}. %% pop_vals(Count, Stack, ValList) -> {ValList,Stack}. %% Pop Count values off the stack and push onto the value list. %% First value is deepest. Always generates list. pop_vals(0, Stk) -> {[],Stk}; pop_vals(C, [Vtail|Stk]) -> %This a list tail pop_vals(C-1, Stk, Vtail). pop_vals(0, Stk, Vs) -> {Vs,Stk}; pop_vals(1, [V|Stk], Vs) -> {[V|Vs],Stk}; pop_vals(2, [V2,V1|Stk], Vs) -> {[V1,V2|Vs],Stk}; pop_vals(C, [V2,V1|Stk], Vs) -> pop_vals(C-2, Stk, [V1,V2|Vs]). %% push_vals(Count, ValList, Stack) -> Stack. %% Push Count values from ValList onto the stack. First value is %% deepest. Fill with 'nil' if not enough values. push_vals(0, _, Stk) -> Stk; push_vals(C, [V|Vs], Stk) -> push_vals(C-1, Vs, [V|Stk]); push_vals(C, [], Stk) -> push_vals(C-1, [], [nil|Stk]). %% push_args(Varlist, ArgList, Stack) -> Stack. %% Use Varlist to push args from ArgList onto the stack. First arg is %% deepest. Tail of VarList determines whether there are varargs. push_args([_V|Vs], [A|As], Stk) -> push_args(Vs, As, [A|Stk]); push_args([_V|Vs], [], Stk) -> push_args(Vs, [], [nil|Stk]); push_args([], _As, Stk) -> Stk; %Drop the rest push_args(_V, As, Stk) -> %Varargs ... save as list [As|Stk]. %% do_set_key(Instrs, LocalVars, Stack, Env, State, Table, Key, Val) -> %% ReturnFromEmul. %% do_get_key(Instrs, LocalVars, Stack, Env, State, Table, Key) -> %% ReturnFromEmul. do_set_key(Is, Cont, Lvs, Stk, Env, Cs, St0, Tab, Key, Val) -> %% We must handle the metamethod and error here. case luerl_heap:set_table_key(Tab, Key, Val, St0) of {value,_,St1} -> emul(Is, Cont, Lvs, Stk, Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?POP|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end. do_get_key(Is, Cont, Lvs, Stk, Env, Cs, St0, Tab, Key) -> %% We must handle the metamethod and error here. case luerl_heap:get_table_key(Tab, Key, St0) of {value,Val,St1} -> emul(Is, Cont, Lvs, [Val|Stk], Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?SINGLE|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end. %% do_op1(Instrs, LocalVars, Stack, Env, State, Op) -> ReturnFromEmul. %% do_op2(Instrs, LocalVars, Stack, Env, State, Op) -> ReturnFromEmul. do_op1(Is, Cont, Lvs, [A|Stk], Env, Cs, St0, Op) -> %% We must handle the metamethod and error here. case op(Op, A, St0) of {value,Res,St1} -> emul(Is, Cont, Lvs, [Res|Stk], Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?SINGLE|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end. do_op2(Is, Cont, Lvs, [A2,A1|Stk], Env, Cs, St0, Op) -> %% We must handle the metamethod and error here. case op(Op, A1, A2, St0) of {value,Res,St1} -> emul(Is, Cont, Lvs, [Res|Stk], Env, Cs, St1); {meta,Meth,Args,St1} -> emul([?FCALL,?SINGLE|Is], Cont, Lvs, [Args,Meth|Stk], Env, Cs, St1); {error,Error,St1} -> lua_error(Error, St1#luerl{stk=Stk,cs=Cs}) end. %% do_break(LocalVars, CallStack, State) -> . do_break(Lvs0, Cs0, St) -> {Bf,Cs1} = find_loop_frame(Cs0, St), #loop_frame{is=Is,cont=Cont,lvs=Lvs1,stk=Stk,env=Env} = Bf, %% Trim the new local variable stack down to original length. Lvs2 = lists:nthtail(length(Lvs0)-length(Lvs1), Lvs0), emul(Is, Cont, Lvs2, Stk, Env, Cs1, St). %% do_return(ArgCount, Stack, Callstack, State) -> . do_return(Ac, Stk0, Cs0, St0) -> {Cf,Cs1} = find_call_frame(Cs0, St0), %Find the first call frame {Ret,Stk1} = pop_vals(Ac, Stk0), %% When tracing bring the state up to date and call the tracer. Tfunc = St0#luerl.trace_func, St1 = if is_function(Tfunc) -> Tfunc(?RETURN(Ret), St0#luerl{stk=Stk1,cs=Cs1}); true -> St0 end, #call_frame{is=Is,cont=Cont,lvs=Lvs,env=Env} = Cf, emul(Is, Cont, Lvs, [Ret|Stk1], Env, Cs1, St1#luerl{cs=Cs1}). find_call_frame([#call_frame{}=Cf|Cs], _St) -> {Cf,Cs}; find_call_frame([_|Cs], St) -> find_call_frame(Cs, St). find_loop_frame([#current_line{}|Cs], St) -> %Skip current line info find_loop_frame(Cs, St); find_loop_frame([#loop_frame{}=Bf|Cs], _St) -> {Bf,Cs}; find_loop_frame(Cs, St) -> lua_error({illegal_op,break}, St#luerl{cs=Cs}). %% do_current_line(Instrs, Continuation, LocalVars, Stack, Env, Stack, State, %% Line, File). do_current_line(Is, Cont, Lvs, Stk, Env, Cs0, St0, Line, File) -> Cs1 = push_current_line(Cs0, Line, File), %Push onto callstack %% When tracing bring the state up to date and call the tracer. Tfunc = St0#luerl.trace_func, St1 = if is_function(Tfunc) -> Tfunc(?CURRENT_LINE(Line, File), St0#luerl{stk=Stk,cs=Cs1}); true -> St0 end, emul(Is, Cont, Lvs, Stk, Env, Cs1, St1). %% push_current_line(CallStack, CurrLine, FileName) -> CallStack. %% Push the current line info on the stack replacing an existing one %% on the top. push_current_line([#current_line{}|Cs], Line, File) -> [#current_line{line=Line,file=File}|Cs]; push_current_line(Cs, Line, File) -> [#current_line{line=Line,file=File}|Cs]. %% do_fcall(Instrs, LocalVars, Stack, Env, State) -> ReturnFromEmul. %% Pop arg list and function from stack and do call. do_fcall(Is, Cont, Lvs, [Args,Func|Stk], Env, Cs, St) -> functioncall(Is, Cont, Lvs, Stk, Env, Cs, St, Func, Args). %% functioncall(Instrs, Cont, LocalVars, Stk, Env, CallStack, State, Func, Args) -> %% %% This is called from within code and continues with Instrs after %% call. It must move everything into State. functioncall(Is, Cont, Lvs, Stk, Env, Cs0, St, Func, Args) -> Fr = #call_frame{func=Func,args=Args,lvs=Lvs,env=Env,is=Is,cont=Cont}, Cs1 = [Fr|Cs0], call_function(Func, Args, Stk, Cs1, St). %% do_tail_fcall(Instrs, Cont, LocalVars, Stack, Env, State) -> %% ReturnFromEmul. do_tail_fcall(_Is, _Cont, _Lvs, [Args,Func|_Stk], _Env, Cs, St) -> error({tail_fcall,Func,Args,Cs,St}). %% do_mcall(Instrs, Cont, LocalVars, Stack, Env, State, Method) -> do_mcall(Is, Cont, Lvs, [Args,Obj|Stk], Env, Cs, St, M) -> methodcall(Is, Cont, Lvs, Stk, Env, Cs, St, Obj, M, Args). %% methodcall(Instrs, Cont, Var, Stk, Env, State, Object, Method, Args) -> %% %% This is called from within code and continues with Instrs after %% call. It must move everything into State. methodcall(Is, Cont, Lvs, Stk, Env, Cs, St0, Obj, Meth, Args) -> %% Get the function to call from object and method. %% We must handle the metamethod and error here. %% io:format("mc1 ~p ~p ~p\n", [Obj,Meth,Args]), case luerl_heap:get_table_key(Obj, Meth, St0) of {value,Func,St1} -> %% io:format("mc2 ~p\n", [Func]), functioncall(Is, Cont, Lvs, Stk, Env, Cs, St1, Func, [Obj|Args]); {meta,Mmeth,Margs,St1} -> %% io:format("mc3 ~p ~p\n", [Mmeth,Margs]), %% Must first meta method to get function and then call it. %% Need to swap to get arguments for call in right order. Is1 = [?FCALL,?SINGLE,?SWAP,?FCALL|Is], emul(Is1, Cont, Lvs, [Margs,Mmeth,[Obj|Args]|Stk], Env, Cs, St1); {error,_Error,St1} -> %No method lua_error({undefined_method,Obj,Meth}, St1#luerl{stk=Stk,cs=Cs}) end. %% do_tail_mcall(Instrs, Cont, LocalVars, Stack, Env, State, Method) -> %% . do_tail_mcall(_Is, _Cont, _Lvs, [Args,Obj|_Stk], _Env, Cs, St, Meth) -> error({tail_mcall,Obj,Meth,Args,Cs,St}). %% call_function(Function, Args, Stack, CallStack, State) -> {Return,State}. %% Setup environment for function and do the actual call. call_function(#funref{env=Env}=Funref, Args, Stk, Cs, St0) -> St1 = trace_call(Funref, Args, Stk, Cs, St0), %% Here we must save the stack in state as function may need it. {Func,St2} = luerl_heap:get_funcdef(Funref, St1#luerl{stk=Stk}), call_luafunc(Func, Args, Stk, Env, Cs, St2); call_function(#erl_func{code=Func}=Funref, Args, Stk, Cs, St0) -> St1 = trace_call(Funref, Args, Stk, Cs, St0), call_erlfunc(Func, Args, Stk, Cs, St1); call_function(#erl_mfa{m=M,f=F,a=A}=Funref, Args, Stk, Cs, St0) -> St1 = trace_call(Funref, Args, Stk, Cs, St0), call_erlmfa({M,F,A}, Args, Stk, Cs, St1); call_function(Func, Args, Stk, Cs, St) -> case luerl_heap:get_metamethod(Func, <<"__call">>, St) of nil -> lua_error({undefined_function,Func}, St#luerl{stk=Stk,cs=Cs}); Meta -> call_function(Meta, [Func|Args], Stk, Cs, St) end. %% trace_call((Function, Args, Stack, CallStack, State) -> State. %% Trace the function call when required. trace_call(Funref, Args, _Stk, _Cs, St) -> %% When tracing bring the state up to date and call the tracer. Tfunc = St#luerl.trace_func, if is_function(Tfunc) -> Tfunc({fcall,Funref,Args}, St); true -> St end. %% call_luafunc(LuaFunc, Args, Stack, Env, CallStack, State) -> {Return,State}. %% Make the local variable and Env frames and push them onto %% respective stacks and call the function. call_luafunc(#lua_func{lsz=Lsz,esz=Esz,pars=_Pars,body=Fis}, Args, Stk0, Env0, Cs, St0) -> L = make_loc_frame(Lsz), {Eref,St1} = make_env_frame(Esz, St0), Lvs = [L], Stk1 = [Args|Stk0], Env1 = [Eref|Env0], %% Tag = St0#luerl.tag, %% io:fwrite("fc: ~p\n", [{Lvs,Env,St0#luerl.env}]), emul(Fis, [], Lvs, Stk1, Env1, Cs, St1). %% call_erlfunc(ErlFunc, Args, Stack, CallStack, State) -> {Return,State}. %% Here we must save the stacks in state as function may need it. %% Note we leave the call frame to the erlang function on the call %% stack. It is popped when we return. call_erlfunc(Func, Args, Stk, Cs0, #luerl{stk=Stk0}=St0) -> case Func(Args, St0#luerl{stk=Stk,cs=Cs0}) of %% {Ret,#luerl{}=St1} when is_list(Ret) -> {Ret,St1} when is_list(Ret) -> [#call_frame{is=Is,cont=Cont,lvs=Lvs,env=Env}|Cs1] = Cs0, emul(Is, Cont, Lvs, [Ret|Stk], Env, Cs1, St1#luerl{stk=Stk0,cs=Cs1}); {lua_error, Reason, St1} -> lua_error(Reason, St1); _Other -> %% Don't include the erl_func in the call stack. lua_error(illegal_return_value, St0#luerl{stk=Stk0,cs=tl(Cs0)}) end. %% call_erlmfa({M,F,A}, Args, Stack, CallStack, State) -> {Return,State}. %% We have two sets of arguments here: the "configured" ones that always %% get passed to the function (A) and the "runtime" ones that are specific %% to this call (Args). For simplicity, we combine them into a list so you %% get a ternary function receiving (A, Args, State). call_erlmfa({M,F,A}, Args, Stk, Cs0, #luerl{stk=Stk0}=St0) -> case apply(M, F, [A, Args, St0#luerl{stk=Stk,cs=Cs0}]) of {Ret,St1} when is_list(Ret) -> [#call_frame{is=Is,cont=Cont,lvs=Lvs,env=Env}|Cs1] = Cs0, emul(Is, Cont, Lvs, [Ret|Stk], Env, Cs1, St1#luerl{stk=Stk0,cs=Cs1}); {lua_error, Reason, St1} -> lua_error(Reason, St1); _Other -> %% Don't include the erl_func in the call stack. lua_error(illegal_return_value, St0#luerl{stk=Stk0,cs=tl(Cs0)}) end. %% do_block(Instrs, LocalVars, Stack, Env, State, %% LocalSize, EnvSize, BlockInstrs) -> . %% Local vars may have been updated so must continue with returned %% version. We also continue with returned stack. There should be no %% changes in the env. do_block(Is, Cont, Lvs, Stk, Env, Cs, St0, Lsz, Esz, Bis) -> L = make_loc_frame(Lsz), {Eref,St1} = make_env_frame(Esz, St0), emul(Bis, [Is|Cont], [L|Lvs], Stk, [Eref|Env], Cs, St1). %% do_block_open(Instrs, LocalVars, Stack, Env, State, %% LocalSize, EnvSize) -> . %% Local vars may have been updated so must continue with returned %% version. We also continue with returned stack. There should be no %% changes in the env. do_block_open(Is, Cont, Lvs, Stk, Env, Cs, St0, Lsz, Esz) -> L = make_loc_frame(Lsz), {Eref,St1} = make_env_frame(Esz, St0), emul(Is, Cont, [L|Lvs], Stk, [Eref|Env], Cs, St1). %% do_block_close(Instrs, LocalVars, Stack, Env, State, %% LocalSize, EnvSize) -> . %% Pop the block local variables and environment variables. do_block_close(Is, Cont, [_|Lvs], Stk, [_|Env], Cs, St) -> emul(Is, Cont, Lvs, Stk, Env, Cs, St). make_env_frame(0, St) -> {not_used,St}; make_env_frame(Esz, St) -> luerl_heap:alloc_environment(Esz, St). %{Eref,St}. make_loc_frame(0) -> not_used; make_loc_frame(Lsz) -> erlang:make_tuple(Lsz, nil). %% do_while(Instrs, Cont, LocalVars, Stack, Env, State, WhileEis, WhileBis) -> %% do_while(Is, Cont, Lvs, Stk, Env, Cs0, St, Eis, Wis) -> %% Add the break frame to the call stack. Fr = #loop_frame{lvs=Lvs,stk=Stk,env=Env,is=Is,cont=Cont}, Cs1 = [Fr|Cs0], emul(Eis, [[?WHILE_LOOP(Eis, Wis)|Is]|Cont], Lvs, Stk, Env, Cs1, St). do_while_loop(Is, Cont, Lvs, [Val|Stk], Env, Cs, St, Eis, Wis) -> case boolean_value(Val) of true -> emul(Wis, [Eis,[?WHILE_LOOP(Eis, Wis)|Is]|Cont], Lvs, Stk, Env, Cs, St); false -> emul([?BREAK|Is], Cont, Lvs, Stk, Env, Cs, St) end. %% do_repeat(Instrs, Cont, LocalVars, Stack, Env, State, RepeatInstrs) -> %% %% We know that at the end of the repear instructions the test value %% is calculated. do_repeat(Is, Cont, Lvs, Stk, Env, Cs0, St, Ris) -> %% Add the break frame to the call stack. Fr = #loop_frame{lvs=Lvs,stk=Stk,env=Env,is=Is,cont=Cont}, Cs1 = [Fr|Cs0], emul(Ris, [[?REPEAT_LOOP(Ris)|Is]|Cont], Lvs, Stk, Env, Cs1, St). do_repeat_loop(Is, Cont, Lvs, [Val|Stk], Env, Cs, St, Ris) -> case boolean_value(Val) of true -> emul([?BREAK|Is], Cont, Lvs, Stk, Env, Cs, St); false -> emul(Ris, [[?REPEAT_LOOP(Ris)|Is]|Cont], Lvs, Stk, Env, Cs, St) end. %% do_and_then(Instrs, Continuation,LocalVars, Stack, Env, State, ThenInstrs) -> %% . %% do_or_else(Instrs, Continuation,LocalVars, Stack, Env, State, ElseInstrs) -> %% . do_and_then(Is, Cont, Lvs, [Val|Stk1]=Stk0, Env, Cs, St, Then) -> %% This is an expression and must always leave a value on stack. case boolean_value(Val) of true -> emul(Then, [Is|Cont], Lvs, Stk1, Env, Cs, St); false -> %% Non true value left on stack. emul(Is, Cont, Lvs, Stk0, Env, Cs, St) end. do_or_else(Is, Cont, Lvs, [Val|Stk1]=Stk0, Env, Cs, St, Else) -> %% This is an expression and must always leave a value on stack. case boolean_value(Val) of true -> %% Non false value left on stack. emul(Is, Cont, Lvs, Stk0, Env, Cs, St); false -> emul(Else, [Is|Cont], Lvs, Stk1, Env, Cs, St) end. %% do_if(Instrs, Continuation, LocalVars, Stack, Env, State, TrueInstrs) -> %% . %% Test value on stack to choose whether to do True instructions. do_if_true(Is, Cont, Lvs, [Val|Stk], Env, Cs, St, True) -> case boolean_value(Val) of true -> emul(True, [Is|Cont], Lvs, Stk, Env, Cs, St); false -> emul(Is, Cont, Lvs, Stk, Env, Cs, St) end. %% do_if(Instrs, LocalVars, Stack, Env, State, TrueInstrs, FalseInstrs) -> %% . %% Test value on stack to choose either True or False instructions. do_if(Is, Cont, Lvs0, [Val|Stk0], Env0, Cs, St0, True, False) -> case boolean_value(Val) of true -> emul(True, [Is|Cont], Lvs0, Stk0, Env0, Cs, St0); false -> emul(False, [Is|Cont], Lvs0, Stk0, Env0, Cs, St0) end. %% do_if(Blocks, Else, Lvs, Stk, Env, St) -> %% do_if_blocks(Blocks, Else, Lvs, Stk, Env, St). %% do_if_blocks([{T,B}|Ts], Else, Lvs0, Stk0, Env0, St0) -> %% {Lvs1,[Val|Stk1],Env1,St1} = emul(T, Lvs0, Stk0, Env0, St0), %% case boolean_value(Val) of %% true -> emul(B, Lvs1, Stk1, Env1, St1); %% false -> do_if_blocks(Ts, Lvs1, Stk1, Env1, St1) %% end; %% do_if_blocks([], Else, Lvs, Stk, Env, St) -> %% emul(Else, Lvs, Stk, Env, St). %% do_if_block([?BLOCK(Lsz, Esz, Bis)], Lvs0, Stk0, Env0, St0, Is) -> %% {Lvs1,Stk1,Env1,St1} = do_block(Bis, Lvs0, Stk0, Env0, St0, Lsz, Esz), %% emul(Is, Lvs1, Stk1, Env1, St1); %% do_if_block(Bis, Lvs0, Stk0, Env0, St0, Is) -> %% {Lvs1,Stk1,Env1,St1} = emul(Bis, Lvs0, Stk0, Env0, St0), %% emul(Is, Lvs1, Stk1, Env1, St1). %% do_numfor(Instrs, LocalVars, Stack, Env, State, Varname, FromInstrs) -> %% do_numfor(Is, Cont, Lvs, [Step,Limit,Init|Stk], Env, Cs0, St, _, Fis) -> %% First check if we have numbers. case luerl_lib:args_to_numbers([Init,Limit,Step]) of [I,L,S] -> %% Add the break frame to the call stack. Fr = #loop_frame{lvs=Lvs,stk=Stk,env=Env,is=Is,cont=Cont}, Cs1 = [Fr|Cs0], do_numfor_loop(Is, Cont, Lvs, Stk, Env, Cs1, St, I, L, S, Fis); error -> badarg_error(loop, [Init,Limit,Step], St#luerl{cs=Cs0}) end. do_numfor_loop(Is, Cont, Lvs, Stk, Env, Cs, St, N, Limit, Step, Fis) -> %% itrace_print("nl: ~p\n", [{N,Stk}]), %% Leave the current counter at the top of the stack for code to get. if Step > 0, N =< Limit -> %Keep going emul(Fis, [[?NFOR_LOOP(N+Step, Limit, Step, Fis)|Is]|Cont], Lvs, [N|Stk], Env, Cs, St); Step < 0, N >= Limit -> %Keep going emul(Fis, [[?NFOR_LOOP(N+Step, Limit, Step, Fis)|Is]|Cont], Lvs, [N|Stk], Env, Cs, St); true -> %Done! emul([?BREAK|Is], Cont, Lvs, Stk, Env, Cs, St) end. %% do_genfor(Instrs, LocalVars, Stack, Env, %% CallStack, State, Vars, FromInstrs) -> %% The top of the stack will contain the return values from the explist. do_genfor(Is, Cont, Lvs, [Val|Stk], Env, Cs0, St, _, Fis) -> case Val of %Sneaky, export Func, Data, Var [Func] -> Data = nil, Var = nil; [Func,Data] -> Var = nil; [Func,Data,Var|_] -> ok; Func -> Data = nil, Var = nil end, %% Add the break frame to the call stack. Fr = #loop_frame{lvs=Lvs,stk=Stk,env=Env,is=Is,cont=Cont}, Cs1 = [Fr|Cs0], do_genfor_call(Is, Cont, Lvs, Stk, Env, Cs1, St, Func, Data, Var, Fis). do_genfor_call(Is, Cont, Lvs, Stk, Env, Cs, St, Func, Data, Val, Fis) -> emul([?FCALL,?GFOR_LOOP(Func, Data, Fis)|Is], Cont, Lvs, [[Data,Val],Func|Stk], Env, Cs, St). do_genfor_loop(Is, Cont, Lvs, [Vals|Stk], Env, Cs, St, Func, Data, Fis) -> case boolean_value(Vals) of true -> emul(Fis, [[?GFOR_CALL(Func,Data,hd(Vals),Fis)|Is]|Cont], Lvs, [Vals|Stk], Env, Cs, St); false -> emul([?BREAK|Is], Cont, Lvs, Stk, Env, Cs, St) end. %% build_tab(FieldCount, Index, Stack, State) -> {TableRef,Stack,State}. %% FieldCount is how many Key/Value pairs are on the stack, Index is %% the index of the next value in the acc. build_tab(Fc, I, [Last|Stk0], St0) -> Fs0 = build_tab_last(I, Last), {Fs1,Stk1} = build_tab_loop(Fc, Stk0, Fs0), %% io:fwrite("bt: ~p\n", [{Fc,I,Acc,Fs0,Fs1}]), {Tref,St1} = luerl_heap:alloc_table(Fs1, St0), {Tref,Stk1,St1}. build_tab_last(I, [V|Vs]) -> [{I,V}|build_tab_last(I+1, Vs)]; build_tab_last(_, []) -> []; build_tab_last(_, Last) -> error({boom,build_tab_acc,Last}). build_tab_loop(0, Stk, Fs) -> {Fs,Stk}; build_tab_loop(C, [V,K|Stk], Fs) -> build_tab_loop(C-1, Stk, [{K,V}|Fs]). %% op(Op, Arg, State) -> OpReturn. %% op(Op, Arg1, Arg2, State) -> OpReturn. %% %% OpReturn = {value,Ret,State} | %% {meta,Method,Args,State} | %% {error,Error,State}. %% %% The built-in operators. Always return a single value! op('-', A, St) -> numeric_op('-', A, St, <<"__unm">>, fun (N) -> -N end); op('not', A, St) -> {value,not ?IS_TRUE(A),St}; op('~', A, St) -> integer_op('~', A, St, <<"__bnot">>, fun (N) -> bnot(N) end); op('#', A, St) -> length_op('#', A, St); op(Op, A, St) -> {error,{badarg,Op,[A]},St}. %% Numeric operators. op('+', A1, A2, St) -> numeric_op('+', A1, A2, St, <<"__add">>, fun (N1,N2) -> N1+N2 end); op('-', A1, A2, St) -> numeric_op('-', A1, A2, St, <<"__sub">>, fun (N1,N2) -> N1-N2 end); op('*', A1, A2, St) -> numeric_op('*', A1, A2, St, <<"__mul">>, fun (N1,N2) -> N1*N2 end); op('/', A1, A2, St) -> numeric_op('/', A1, A2, St, <<"__div">>, fun (N1,N2) -> N1/N2 end); %% The '//' and '%' operators are specially handled to avoid first %% converting integers to floats and potentially lose precision. op('//', A1, A2, St) -> numeric_op('//', A1, A2, St, <<"__idiv">>, fun (N1,N2) when is_integer(N1), is_integer(N2) -> Idiv = N1 div N2, Irem = N1 rem N2, if Irem =:= 0 -> Idiv; Idiv < 0 -> Idiv - 1; true -> Idiv end; (N1,N2) -> 0.0 + floor(N1/N2) end); op('%', A1, A2, St) -> numeric_op('%', A1, A2, St, <<"__mod">>, fun (N1,N2) when is_integer(N1), is_integer(N2) -> Irem = N1 rem N2, if (Irem < 0) and (N2 >= 0) -> Irem + N2; (Irem > 0) and (N2 < 0) -> Irem + N2; true -> Irem end; %% if Irem < 0 -> %% if N2 < 0 -> Irem; %% true -> Irem + N2 %% end; %% Irem > 0 -> %% if N2 < 0 -> Irem + N2; %% true -> Irem %% end; %% true -> 0 %Irem =:= 0 %% end; (N1,N2) -> N1 - floor(N1/N2)*N2 end); op('^', A1, A2, St) -> numeric_op('^', A1, A2, St, <<"__pow">>, fun (N1,N2) -> math:pow(N1, N2) end); %% Bitwise operators. %% The '>>' is an arithmetic shift as a logical shift implies a word %% size which we don't have. op('&', A1, A2, St) -> integer_op('&', A1, A2, St, <<"__band">>, fun (N1,N2) -> N1 band N2 end); op('|', A1, A2, St) -> integer_op('|', A1, A2, St, <<"__bor">>, fun (N1,N2) -> N1 bor N2 end); op('~', A1, A2, St) -> integer_op('~', A1, A2, St, <<"__bxor">>, fun (N1,N2) -> N1 bxor N2 end); op('<<', A1, A2, St) -> integer_op('<<', A1, A2, St, <<"__shl">>, fun (N1,N2) -> N1 bsl N2 end); op('>>', A1, A2, St) -> integer_op('>>', A1, A2, St, <<"__shr">>, fun (N1,N2) -> N1 bsr N2 end); %% Relational operators, getting close. op('==', A1, A2, St) -> eq_op('==', A1, A2, St); op('~=', A1, A2, St) -> neq_op('~=', A1, A2, St); op('<=', A1, A2, St) -> le_op('<=', A1, A2, St); op('>=', A1, A2, St) -> le_op('>=', A2, A1, St); op('<', A1, A2, St) -> lt_op('<', A1, A2, St); op('>', A1, A2, St) -> lt_op('>', A2, A1, St); %% String operator. op('..', A1, A2, St) -> concat_op(A1, A2, St); %% Bad args here. op(Op, A1, A2, St) -> {error,{badarg,Op,[A1,A2]}, St}. -ifndef(HAS_FLOOR). %% floor(Number) -> integer(). %% Floor does not exist before 20 so we need to do it ourselves. floor(N) when is_integer(N) -> N; floor(N) when is_float(N) -> round(N - 0.5). -endif. %% length_op(Op, Arg, State) -> OpReturn. %% numeric_op(Op, Arg, State, Event, Raw) -> OpReturn. %% numeric_op(Op, Arg, Arg, State, Event, Raw) -> OpReturn. %% integer_op(Op, Arg, State, Event, Raw) -> OpReturn. %% integer_op(Op, Arg, Arg, State, Event, Raw) -> OpReturn. %% eq_op(Op, Arg, Arg, State) -> OpReturn. %% neq_op(Op, Arg, Arg, State) -> OpReturn. %% lt_op(Op, Arg, Arg, State) -> OpReturn. %% le_op(Op, Arg, Arg, State) -> OpReturn. %% concat_op(Arg, Arg, State) -> OpReturn. %% %% OpReturn = {value,Ret,State} | %% {meta,Method,Args,State} | %% {error,Error,State}. %% %% Together with their metas straight out of the reference %% manual. Note that: %% - numeric_op string args are always floats %% - eq/neq metamethods here must return boolean values and the tests %% themselves are type dependent length_op(_Op, A, St) when is_binary(A) -> {value,byte_size(A),St}; length_op(_Op, A, St) -> case luerl_heap:get_metamethod(A, <<"__len">>, St) of nil -> if ?IS_TREF(A) -> {value,luerl_lib_table:raw_length(A, St),St}; true -> {error,{badarg,'#',[A]}, St} end; Meth -> {meta,Meth,[A],St} end. numeric_op(Op, A, St, E, Raw) -> case luerl_lib:arg_to_number(A) of error -> numeric_op_meta(Op, A, E, St); N -> do_numeric_op(Op, [N], St, Raw) end. numeric_op(Op, A1, A2, St, E, Raw) -> case luerl_lib:args_to_numbers(A1, A2) of [_N1,_N2] = Ns -> do_numeric_op(Op, Ns, St, Raw); error -> numeric_op_meta(Op, A1, A2, E, St) end. integer_op(Op, A, St, E, Raw) -> case luerl_lib:arg_to_integer(A) of error -> numeric_op_meta(Op, A, E, St); N -> do_numeric_op(Op, [N], St, Raw) end. integer_op(Op, A1, A2, St, E, Raw) -> case luerl_lib:args_to_integers(A1, A2) of [_N1,_N2] = Ns -> do_numeric_op(Op, Ns, St, Raw); error -> numeric_op_meta(Op, A1, A2, E, St) end. %% do_numeric_op(Op, Numbers, State, Raw) -> %% {value,Value,State) | {error,Error,State}. do_numeric_op(Op, Ns, St, Raw) -> try {value,apply(Raw, Ns),St} catch ?CATCH(_, _, _) {error,{badarith,Op,Ns},St} end. numeric_op_meta(Op, A, E, St) -> case luerl_heap:get_metamethod(A, E, St) of nil -> {error,{badarith,Op,[A]}, St}; Meth -> {meta,Meth,[A],St} end. numeric_op_meta(Op, A1, A2, E, St) -> case luerl_heap:get_metamethod(A1, A2, E, St) of nil -> {error,{badarith,Op,[A1,A2]},St}; Meth -> {meta,Meth,[A1,A2],St} end. eq_op(_Op, A1, A2, St) when A1 == A2 -> {value,true,St}; eq_op(_Op, A1, A2, St) when ?IS_TREF(A1), ?IS_TREF(A2) ; ?IS_USDREF(A1), ?IS_USDREF(A2) -> case get_eqmetamethod(A1, A2, St) of nil -> {value,false,St}; Meth -> Func = fun (Args, St0) -> {Ret,St1} = functioncall(Meth, Args, St0), {[boolean_value(Ret)],St1} end, {meta,#erl_func{code=Func},[A1,A2],St} end; eq_op(_, _, _, St) -> {value,false,St}. neq_op(_Op, A1, A2, St) when A1 == A2 -> {value,false,St}; neq_op(_Op, A1, A2, St) when ?IS_TREF(A1), ?IS_TREF(A2) ; ?IS_USDREF(A1), ?IS_USDREF(A2) -> case get_eqmetamethod(A1, A2, St) of nil -> {value,true,St}; Meth -> Func = fun (Args, St0) -> {Ret,St1} = functioncall(Meth, Args, St0), {[not boolean_value(Ret)],St1} end, {meta,#erl_func{code=Func},[A1,A2],St} end; neq_op(_, _, _, St) -> {value,true,St}. get_eqmetamethod(A1, A2, St) -> %% Must have "same" metamethod here. How do we test? case luerl_heap:get_metamethod(A1, <<"__eq">>, St) of nil -> nil; Meth -> case luerl_heap:get_metamethod(A2, <<"__eq">>, St) of Meth -> Meth; %Must be the same method _ -> nil end end. lt_op(_Op, A1, A2, St) when is_number(A1), is_number(A2) -> {value,A1 < A2,St}; lt_op(_Op, A1, A2, St) when is_binary(A1), is_binary(A2) -> {value,A1 < A2,St}; lt_op(Op, A1, A2, St) -> op_meta(Op, A1, A2, <<"__lt">>, St). le_op(_Op, A1, A2, St) when is_number(A1), is_number(A2) -> {value,A1 =< A2,St}; le_op(_Op, A1, A2, St) when is_binary(A1), is_binary(A2) -> {value,A1 =< A2,St}; le_op(Op, A1, A2, St) -> %% Must check for first __le then __lt metamethods. case luerl_heap:get_metamethod(A1, A2, <<"__le">>, St) of nil -> %% Try for not (Op2 < Op1) instead. case luerl_heap:get_metamethod(A1, A2, <<"__lt">>, St) of nil -> {error,{badarg,Op,[A1,A2]}, St}; Meth -> {meta,Meth,[A2,A1],St} end; Meth -> {meta,Meth,[A1,A2],St} end. concat_op(A1, A2, St) -> case luerl_lib:conv_list([A1,A2], [lua_string,lua_string]) of [S1,S2] -> {value,<>,St}; error -> op_meta('..', A1, A2, <<"__concat">>, St) end. %% op_meta(Op, A, E, St) -> %% case luerl_heap:get_metamethod(A, E, St) of %% nil -> {error,{badarg,Op,[A]}, St}; %% Meth -> {meta,Meth,[A],St} %% end. op_meta(Op, A1, A2, E, St) -> case luerl_heap:get_metamethod(A1, A2, E, St) of nil -> {error,{badarg,Op,[A1,A2]},St}; Meth -> {meta,Meth,[A1,A2],St} end. %% boolean_value(Rets) -> boolean(). %% Return the "boolean" value of a value/function return list. boolean_value([nil|_]) -> false; boolean_value([false|_]) -> false; boolean_value([_|_]) -> true; boolean_value([]) -> false; boolean_value(nil) -> false; boolean_value(false) -> false; boolean_value(_) -> true. %% first_value(Rets) -> Value. %% multiple_value(Value) -> [Value]. first_value([V|_]) -> V; first_value([]) -> nil. multiple_value(V) when not is_list(V) -> [V]. rvirding-luerl-772b25e/src/luerl_lib_os.erl0000664000232200023220000002355415114130670021342 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_lib_os.erl %% Author : Robert Virding %% Purpose : The os library for Luerl. -module(luerl_lib_os). -include("luerl.hrl"). ?MODULEDOC(false). -export([install/1, clock/3, date/3, difftime/3, execute/3, lua_exit/3, getenv/3, remove/3, rename/3, time/3, tmpname/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this %% For `remove/2'. -include_lib("kernel/include/file.hrl"). %% For `tmpname/2' in `luerl_lib_os'. -define(TMPNAM_MAXTRIES, 100). -define(TMPNAM_TEMPLATE(S), "/tmp/lua_" ++ S). install(St) -> luerl_heap:alloc_table(table(), St). table() -> [{<<"clock">>,#erl_mfa{m=?MODULE,f=clock}}, {<<"date">>,#erl_mfa{m=?MODULE,f=date}}, {<<"difftime">>,#erl_mfa{m=?MODULE,f=difftime}}, {<<"execute">>,#erl_mfa{m=?MODULE,f=execute}}, {<<"exit">>,#erl_mfa{m=?MODULE,f=lua_exit}}, {<<"getenv">>,#erl_mfa{m=?MODULE,f=getenv}}, {<<"remove">>,#erl_mfa{m=?MODULE,f=remove}}, {<<"rename">>,#erl_mfa{m=?MODULE,f=rename}}, {<<"time">>,#erl_mfa{m=?MODULE,f=time}}, {<<"tmpname">>,#erl_mfa{m=?MODULE,f=tmpname}}]. getenv(_, [<<>>|_], St) -> {[nil],St}; getenv(_, [A|_], St) when is_binary(A) ; is_number(A) -> case os:getenv(luerl_lib:arg_to_list(A)) of Env when is_list(Env) -> {[list_to_binary(Env)],St}; false -> {[nil],St} end; getenv(_, As, St) -> badarg_error(getenv, As, St). %% execute([Command|_], State) -> {[Ret,Type,Stat],State}. %% Execute a command and get the return code. We cannot yet properly %% handle if our command terminated with a signal. execute(_, [], St) -> {true,St}; %We have a shell execute(_, [A|_], St) -> case luerl_lib:arg_to_string(A) of S when is_binary(S) -> Opts = [{arg0,"sh"},{args,["-c", S]}, hide,in,eof,exit_status,use_stdio,stderr_to_stdout], P = open_port({spawn_executable,"/bin/sh"}, Opts), N = execute_handle(P), Ret = if N =:= 0 -> true; %Success true -> nil %Error end, {[Ret,<<"exit">>,N],St}; error -> badarg_error(execute, [A], St) end; execute(_, As, St) -> badarg_error(execute, As, St). execute_handle(P) -> receive {P,{data,D}} -> %% Print stdout/stderr like Lua does. io:put_chars(D), execute_handle(P); {P, {exit_status,N}} -> %% Wait for the eof then close the port. receive {P, eof} -> port_close(P), N end end. %% exit([ExitCode,CloseState|_], State) -> nil. %% Exit the host program. If ExitCode is true, the return code is 0; %% if ExitCode is false, the return code is 1; if ExitCode is a number, the %% return code is this number. The default value for ExitCode is true. %% NOT IMPLEMENTED: %% If the optional second argument CloseState is true, it will close the Lua %% state before exiting. lua_exit(_, [], St) -> lua_exit(nil, [true,false], St); lua_exit(_, [C], St) -> lua_exit(nil, [C,false], St); lua_exit(_, [Co0|_], St) -> %% lua_exit([Co0,Cl0], St) -> Co1 = case luerl_lib:arg_to_number(Co0) of X when is_integer(X) -> X; error -> case Co0 of false -> 1; true -> 0; error -> badarg_error(exit, [Co0], St) end end, %% Uncomment this if you need the second argument to determine whether to %% destroy the Lua state or not. %% Cl1 = case Cl0 of %% true -> true; %% false -> false; %% _ -> badarg_error(exit, [Cl0], St) %% end, erlang:halt(Co1). %% tmpname([], State) %% Faithfully recreates `tmpnam'(3) in lack of a NIF. tmpname(_, [_|_], St) -> %% Discard extra arguments. tmpname(nil, [], St); tmpname(_, [], St) -> Out = tmpname_try(randchar(6, []), 0), %% We make an empty file the programmer will have to close themselves. %% This is done for security reasons. file:write_file(Out, ""), {[list_to_binary(Out)],St}. %% Support function for `tmpname/2' - generates a random filename following a %% template. tmpname_try(_, ?TMPNAM_MAXTRIES) -> %% Exhausted... false; tmpname_try(A, N) -> case file:read_file_info(?TMPNAM_TEMPLATE(A)) of {error,enoent} -> ?TMPNAM_TEMPLATE(A); %% Success, at last! _ -> tmpname_try(randchar(6, []), N+1) end. %% Support function for `tmpname_try/2'. randchar(0, A) -> A; randchar(N, A) -> randchar(N-1, [rand:uniform(26)+96|A]). %% rename([Source,Destination|_], State) %% Renames the file or directory `Source' to `Destination'. If this function %% fails, it returns `nil', plus a string describing the error code and the %% error code. Otherwise, it returns `true'. rename(_, [S,D|_], St) -> case {luerl_lib:arg_to_string(S), luerl_lib:arg_to_string(D)} of {S1,D1} when is_binary(S1) , is_binary(D1) -> case file:rename(S1,D1) of ok -> {[true],St}; {error,R} -> #{errno := En, errstr := Er} = luerl_util:errname_info(R), {[nil,Er,En],St} end; %% These are for throwing a `badmatch' error on the correct argument. {S1,D1} when not is_binary(S1) , not is_binary(D1) -> badarg_error(rename, [S1,D1], St); {S1,D1} when not is_binary(S1) , is_binary(D1) -> badarg_error(rename, [S1], St); {S1,D1} when is_binary(S1) , not is_binary(D1) -> badarg_error(rename, [D1], St) end; rename(_, As, St) -> badarg_error(rename, As, St). %% remove([Path|_], State) %% Deletes the file (or empty directory) with the given `Path'. If this %% function fails, it returns `nil' plus a string describing the error, and the %% error code. Otherwise, it returns `true'. remove(_, [A|_], St) -> case luerl_lib:arg_to_string(A) of A1 when is_binary(A1) -> %% Emulate the underlying call to `remove(3)'. case file:read_file_info(A1) of {ok,#file_info{type=T}} when T == directory ; T == regular -> %% Select the corresponding function. Op = if T == directory -> del_dir; true -> delete end, case file:Op(A) of ok -> {[true],St}; {error,R} -> {remove_geterr(R, A), St} end; {error,R} -> %% Something went wrong. {remove_geterr(R, A), St} end; error -> badarg_error(remove, [A], St) end; remove(_, As, St) -> badarg_error(remove, As, St). %% Utility function to get a preformatted list to return from `remove/2'. remove_geterr(R, F) -> F1 = binary_to_list(F), #{errno := En, errstr := Er} = luerl_util:errname_info(R), [nil, list_to_binary(F1 ++ ": " ++ Er), En]. %% Time and date functions. clock(_, As, St) -> Type = case As of %Choose which we want [<<"runtime">>|_] -> runtime; _ -> wall_clock end, {Tot,_} = erlang:statistics(Type), %Milliseconds {[Tot*1.0e-3],St}. date(ConfArg, [], St) -> date(ConfArg, [<<"%c">>], St); date(ConfArg, [Fmt], St) when is_binary(Fmt) -> date(ConfArg, [Fmt, current_timestamp()], St); date(_, [Fmt, TimeStamp], St) when is_binary(Fmt) and is_number(TimeStamp) -> DateTime = timestamp_to_datetime(TimeStamp), Formatted = luerl_lib_os_date:format(DateTime, Fmt), {Enc, St1} = luerl:encode(Formatted, St), {[Enc],St1}; date(_, As, St) -> badarg_error(date, As, St). difftime(_, [T2,T1|_], St) -> {[T2 - T1],St}; difftime(_, As, St) -> badarg_error(difftime, As, St). time(_, As=[#tref{}=Tref], St) -> L = luerl:decode(Tref, St), compute_time(proplists:to_map(L), As, St); time(_, _, St) -> %Time since 1 Jan 1970 {[current_timestamp()],St}. compute_time(Map=#{<<"year">> := Y, <<"month">> := Mth, <<"day">> := D}, _, St) -> H = maps:get(<<"hour">>, Map, 12), Min = maps:get(<<"min">>, Map, 0), S = maps:get(<<"sec">>, Map, 0), LocalEpoch = calendar:universal_time_to_local_time({{1970,1,1},{0,0,0}}), Result = calendar:datetime_to_gregorian_seconds({{Y, Mth, D}, {H, Min, S}}) - calendar:datetime_to_gregorian_seconds(LocalEpoch), {[Result],St}; compute_time(Map, _As, St) -> MissingArg = lists:foldl(fun(K,Acc=undefined) -> case maps:is_key(K, Map) of false -> K; true -> Acc end; (_,Acc) -> Acc end, undefined, [<<"day">>, <<"month">>, <<"year">>]), badarg_error(time, MissingArg, St). current_timestamp() -> {Mega,Sec,Micro} = os:timestamp(), 1.0e6*Mega+Sec+Micro*1.0e-6. timestamp_to_datetime(Timestamp) -> SecondsSinceEpoch = round(Timestamp), calendar:system_time_to_local_time(SecondsSinceEpoch, second). rvirding-luerl-772b25e/src/luerl_comp_lint.erl0000664000232200023220000001726115114130670022055 0ustar debalancedebalance%% Copyright (c) 2019 Robert Virding %% %% 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. %% File : luerl_lint.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 error checker for Luerl. %% There isn't much checking which can be done here as most is allowed %% and generates run-time errors or strangeness. So far all we can %% check is the legal use of varargs ... and warn if assignments have %% different number of variables and expressions. -module(luerl_comp_lint). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). -export([chunk/2,format_error/1]). -record(lint, {opts=[], %Options pars=[], %Local function parameters errors=[], %Errors warnings=[] %Warnings }). %% format_error(Error) -> String. format_error(illegal_varargs) -> "cannot use '...' outside a vararg function"; format_error(assign_mismatch) -> "assign mismatch variables and expressions". %% chunk(Code, Compinfo) -> {ok,Warnings} | {error,Errors,Warnings}. chunk(Code, #cinfo{opts=Opts}=_Ci) -> St0 = #lint{opts=Opts}, St1 = functiondef(Code, St0), return_status(St1). return_status(#lint{errors=[]}=St) -> {ok,St#lint.warnings}; return_status(#lint{errors=Es,warnings=Ws}) -> {error,Es,Ws}. %% stmts(Stmts, State) -> State. stmts([S|Ss], St0) -> St1 = stmt(S, St0), stmts(Ss, St1); stmts([], St) -> St. %% stmt(Stmt, State) -> State. stmt(#assign_stmt{}=A, St) -> assign_stmt(A, St); stmt(#call_stmt{}=C, St) -> call_stmt(C, St); stmt(#return_stmt{}=R, St) -> return_stmt(R, St); stmt(#break_stmt{}, St) -> St; stmt(#block_stmt{}=B, St) -> block_stmt(B, St); stmt(#while_stmt{}=W, St) -> while_stmt(W, St); stmt(#repeat_stmt{}=R, St) -> repeat_stmt(R, St); stmt(#if_stmt{}=If, St) -> if_stmt(If, St); stmt(#nfor_stmt{}=For, St) -> numfor_stmt(For, St); stmt(#gfor_stmt{}=For, St) -> genfor_stmt(For, St); stmt(#local_assign_stmt{}=For, St) -> local_assign_stmt(For, St); stmt(#local_fdef_stmt{}=For, St) -> local_fdef_stmt(For, St); stmt(#expr_stmt{}=E, St) -> expr_stmt(E, St). %% assign_stmt(Assign, State) -> State. %% call_stmt(Call, State) -> State. %% return_stmt(Return, State) -> State. %% block_stmt(Block, State) -> State. %% while_stmt(While, State) -> State. %% repeat_stmt(Repeat, State) -> State. %% if_stmt(If, State) -> State. %% numfor_stmt(Numfor, State) -> State. %% genfor_stmt(Genfor, State) -> State. %% local_assign_stmt(Assign, State) -> State. %% local_fdef_stmt(Fdef, State) -> State. %% expr_stmt(Expr, State) -> State. assign_stmt(#assign_stmt{vars=Vs,exps=Es}, St0) -> %% Must work more on this to get it right. %% St1 = ?IF(length(Vs) =/= length(Es), %% assign_mismatch_warning(Anno, St0), St0), St1 = St0, St2 = lists:foldl(fun (V, S) -> assign_var(V, S) end, St1, Vs), explist(Es, St2). assign_var(#dot{exp=Exp,rest=Rest}, St0) -> St1 = prefixexp_first(Exp, St0), assign_var_rest(Rest, St1); assign_var(#var{l=Anno,name='...'}, St) -> %% Not allowed to bind ... . illegal_varargs_error(Anno, St); assign_var(_Var, St) -> St. assign_var_rest(#dot{exp=Exp,rest=Rest}, St0) -> St1 = prefixexp_element(Exp, St0), assign_var_rest(Rest, St1); assign_var_rest(Exp, St) -> assign_var_last(Exp, St). assign_var_last(#key{key=Exp}, St) -> exp(Exp, St). call_stmt(#call_stmt{call=Exp}, St) -> exp(Exp, St). return_stmt(#return_stmt{exps=Es}, St) -> explist(Es, St). block_stmt(#block_stmt{body=Ss}, St) -> stmts(Ss, St). while_stmt(#while_stmt{exp=Exp,body=Ss}, St0) -> St1 = exp(Exp, St0), block(Ss, St1). repeat_stmt(#repeat_stmt{body=Ss}, St) -> block(Ss, St). if_stmt(#if_stmt{tests=Ts,else_block=Else}, St0) -> Fun = fun ({E,B}, S0) -> S1 = exp(E, S0), block(B, S1) end, St1 = lists:foldl(Fun, St0, Ts), block(Else, St1). numfor_stmt(#nfor_stmt{init=I,limit=L,step=S,body=B}, St0) -> St1 = explist([I,L,S], St0), block(B, St1). genfor_stmt(#gfor_stmt{gens=Gs,body=B}, St0) -> St1 = explist(Gs, St0), block(B, St1). local_assign_stmt(#local_assign_stmt{exps=Es}, St0) -> %% Must work more on this to get it right. %% St1 = ?IF(length(Vs) =/= length(Es), %% assign_mismatch_warning(Anno, St0), St0), St1 = St0, explist(Es, St1). local_fdef_stmt(#local_fdef_stmt{func=F}, St) -> functiondef(F, St). expr_stmt(#expr_stmt{exp=Exp}, St) -> exp(Exp, St). %% block(Block, State) -> State. block(#block{body=Ss}, St) -> stmts(Ss, St). %% explist(Exprs, State) -> State. %% exp(Expr, State) -> State. %% prefixexp(Expr, State) -> State. explist(Es, St) -> lists:foldl(fun (E, S) -> exp(E, S) end, St, Es). exp(#lit{}, St) -> St; exp(#fdef{}=F, St) -> functiondef(F, St); exp(#op{args=Es}, St) -> explist(Es, St); exp(#tabcon{fields=Fs}, St) -> tableconstructor(Fs, St); exp(E, St) -> prefixexp(E, St). prefixexp(#dot{exp=Exp,rest=Rest}, St0) -> St1 = prefixexp_first(Exp, St0), prefixexp_rest(Rest, St1); prefixexp(Exp, St) -> prefixexp_first(Exp, St). prefixexp_first(#single{exp=Exp}, St) -> exp(Exp, St); prefixexp_first(#var{}=V, St) -> var(V, St). prefixexp_rest(#dot{exp=Exp,rest=Rest}, St0) -> St1 = prefixexp_element(Exp, St0), prefixexp_rest(Rest, St1); prefixexp_rest(Exp, St) -> prefixexp_element(Exp, St). prefixexp_element(#key{key=Exp}, St) -> exp(Exp, St); prefixexp_element(#fcall{args=Es}, St) -> explist(Es, St); prefixexp_element(#mcall{meth=Lit,args=Es}, St0) -> St1 = lit(Lit, St0), explist(Es, St1). %% functiondef(FuncDef, State) -> State. functiondef(#fdef{pars=Ps,body=Ss}, #lint{pars=Pars}=St0) -> St1 = St0#lint{pars=Ps}, %Use current parameters St2 = stmts(Ss, St1), St2#lint{pars=Pars}. %Reset previous parameters %% tableconstructor(Fields, State) -> State. tableconstructor(Fs, St) -> Fun = fun (#efield{val=Exp}, S) -> exp(Exp, S); (#kfield{key=Key,val=Val}, S0) -> S1 = exp(Key, S0), exp(Val, S1) end, lists:foldl(Fun, St, Fs). %% var(Var, State) -> State. var(#var{l=Anno,name='...'}, St) -> case lists:keymember('...', #var.name, St#lint.pars) of true -> St; false -> illegal_varargs_error(Anno, St) end; var(_Var, St) -> St. %% lit(Lit, State) -> State. lit(#lit{l=Anno,val='...'}, St) -> case lists:keymember('...', #var.name, St#lint.pars) of true -> St; false -> illegal_varargs_error(Anno, St) end; lit(_Lit, St) -> St. %% add_error(Annotation, Error, State) -> State. %% add_warning(Annotation, Warning, State) -> State. %% Add errors/warnings to the state. add_error(Anno, E, #lint{errors=Errs}=St) -> L = luerl_anno:line(Anno), St#lint{errors=Errs ++ [{L,?MODULE,E}]}. %% add_warning(Anno, W, #lint{warnings=Warns}=St) -> %% L = luerl_anno:line(Anno), %% St#lint{warnings=Warns ++ [{L,?MODULE,W}]}. illegal_varargs_error(Anno, St) -> add_error(Anno, illegal_varargs, St). %% assign_mismatch_warning(Anno, St) -> %% add_warning(Anno, assign_mismatch, St). rvirding-luerl-772b25e/src/luerl_comp_cg.erl0000664000232200023220000004473515114130670021506 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : luerl_comp_cg.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Does code generation in the compiler. In the generated function %% definitions annotations and when we generate the #current_line{} %% instruction we use the virtual filename the compiler has %% generated. This is either the default file name or an explicit one %% given with the {file,FileName} compiler option. -module(luerl_comp_cg). -include("luerl.hrl"). -include("luerl_comp.hrl"). -include("luerl_instrs.hrl"). ?MODULEDOC(false). -export([chunk/2]). -import(ordsets, [add_element/2,is_element/2,union/1,union/2, subtract/2,intersection/2,new/0]). -record(c_cg, {line, %Current line vfile=[] %Current virtual file }). %% chunk(Code, CompInfo) -> {ok,Code}. %% Return a list of instructions to define the chunk function. chunk(Code0, #cinfo{vfile=Vfile,opts=Opts}=_Ci) -> St0 = #c_cg{line=0,vfile=Vfile}, %Get the virtual filename {Code1,_} = functiondef(Code0, St0), luerl_comp:debug_print(Opts, "cg: ~p\n", [Code1]), {ok,Code1}. %% set_var(Var) -> SetIs. %% get_var(Var) -> GetIs. %% These return a LIST of instructions for setting/getting variable. set_var(#lvar{d=D,i=I}) -> [?STORE_LVAR(D, I)]; set_var(#evar{d=D,i=I}) -> [?STORE_EVAR(D, I)]; set_var(#gvar{n=N}) -> [?STORE_GVAR(N)]. get_var(#lvar{d=D,i=I}) -> [?PUSH_LVAR(D, I)]; get_var(#evar{d=D,i=I}) -> [?PUSH_EVAR(D, I)]; get_var(#gvar{n=N}) -> [?PUSH_GVAR(N)]. %% stmt(Stmts, State) -> {Istmts,State}. stmts([S0|Ss0], St0) -> %% We KNOW that the annotation is the second element. Line = luerl_anno:line(element(2, S0)), {CurLine,St1} = add_current_line(Line, St0), {S1,St2} = stmt(S0, nul, St1), %% io:format("ss1: ~p\n", [{Loc0,Free0,Used0}]), {Ss1,St3} = stmts(Ss0, St2), {CurLine ++ S1 ++ Ss1,St3}; stmts([], St) -> {[],St}. %% add_current_line(Line, State) -> {CurLine,State}. %% Return currentline instruction and update state if new line. add_current_line(Line, #c_cg{line=Line}=St) -> {[],St}; add_current_line(Line, #c_cg{vfile=Vfile}=St) -> {[?CURRENT_LINE(Line, Vfile)],St#c_cg{line=Line}}. %% stmt(Stmt, LocalVars, State) -> {Istmt,State}. stmt(#assign_stmt{}=A, _, St) -> assign_stmt(A, St); stmt(#call_stmt{}=C, _, St) -> call_stmt(C, St); stmt(#return_stmt{}=R, _, St) -> return_stmt(R, St); stmt(#break_stmt{}, _, St) -> {[?BREAK],St}; stmt(#block_stmt{}=B, _, St) -> block_stmt(B, St); stmt(#while_stmt{}=W, _, St) -> while_stmt(W, St); stmt(#repeat_stmt{}=R, _, St) -> repeat_stmt(R, St); stmt(#if_stmt{}=I, _, St) -> if_stmt(I, St); stmt(#nfor_stmt{}=F, _, St) -> numfor_stmt(F, St); stmt(#gfor_stmt{}=F, _, St) -> genfor_stmt(F, St); stmt(#local_assign_stmt{}=L, _, St) -> local_assign_stmt(L, St); stmt(#local_fdef_stmt{}=L, _, St) -> local_fdef_stmt(L, St); stmt(#expr_stmt{}=E, _, St) -> expr_stmt(E, St). %% assign_stmt(Assign, State) -> {AssignIs,State}. %% We must evaluate all expressions, even the unneeded ones. assign_stmt(#assign_stmt{vars=Vs,exps=Es}, St) -> assign_loop(Vs, Es, St). %% assign_loop(Vars, Exps, State) -> {Iassigns,State}. %% Must be careful with pushing and popping values here. Make sure %% all non-last values are singleton. %% %% This could most likely be folded together with assign_local_loop/3. assign_loop([V], [E], St0) -> %Remove unnecessary ?PUSH_VALS {Ie,St1} = exp(E, single, St0), %Last argument to one variable {Iv,St2} = assign_prefixexp(V, St1), {Ie ++ Iv,St2}; assign_loop([V|Vs], [E], St0) -> {Ie,St1} = exp(E, multiple, St0), %Last argument to rest of vars {Ias,St2} = assign_loop_var(Vs, St1), {Iv,St3} = assign_prefixexp(V, St2), {Ie ++ Ias ++ Iv,St3}; assign_loop([V|Vs], [E|Es], St0) -> {Ie,St1} = exp(E, single, St0), %Not last argument! {Ias,St2} = assign_loop(Vs, Es, St1), {Iv,St3} = assign_prefixexp(V, St2), {Ie ++ Ias ++ Iv,St3}; assign_loop([], Es, St) -> assign_loop_exp(Es, St). %% assign_loop_var(Vars, State) -> {Iassigns,State}. %% Extract necessary number of values from value list on stack. Pad %% with nil. assign_loop_var(Vs, St) -> assign_loop_var(Vs, 1, St). assign_loop_var([V|Vs], Vc, St0) -> {Ias,St1} = assign_loop_var(Vs, Vc+1, St0), {Iv,St2} = assign_prefixexp(V, St1), {Ias ++ Iv,St2}; assign_loop_var([], Vc, St) -> {[?PUSH_VALS(Vc)],St}. %% assign_loop_exp(Exprs, State) -> {Instrs,State}. %% Evaluate remaining expressions and pop the values from the stack. assign_loop_exp([E|Es], St0) -> {Ie,St1} = exp(E, single, St0), %It will be dropped anyway {Ias,St2} = assign_loop_exp(Es, St1), {Ie ++ Ias ++ [?POP],St2}; %Pop unneeded value off stack assign_loop_exp([], St) -> {[],St}. %% assign_prefixexp(Expr, State) -> {Instrs,State}. %% assign_prefixexp_rest(Expr, State) -> {Instrs,State}. %% assign_prefixexp_element(Expr, State) -> {Instrs,State}. assign_prefixexp(#dot{exp=Exp,rest=Rest}, St0) -> {Ie,St1} = prefixexp_first(Exp, single, St0), {Ir,St2} = assign_prefixexp_rest(Rest, St1), {Ie ++ Ir,St2}; assign_prefixexp(V, St) -> {set_var(V),St}. assign_prefixexp_rest(#dot{exp=Exp,rest=Rest}, St0) -> {Ie,St1} = prefixexp_element(Exp, single, St0), {Ir,St2} = assign_prefixexp_rest(Rest, St1), {Ie ++ Ir,St2}; assign_prefixexp_rest(Exp, St) -> assign_prefixexp_element(Exp, St). assign_prefixexp_element(#key{key=#lit{val=K}}, St) -> {[?SET_LIT_KEY(K)],St}; %[?PUSH_LIT(K),?SET_KEY] assign_prefixexp_element(#key{key=Exp}, St0) -> {Ie,St1} = exp(Exp, single, St0), {Ie ++ [?SET_KEY],St1}. %% call_stmt(Call, State) -> {CallIs,State}. %% Must pop function return value list from stack. call_stmt(#call_stmt{call=Exp}, St0) -> {Ie,St1} = exp(Exp, multiple, St0), {Ie ++ [?POP],St1}. %% return_stmt(Return, State) -> {ReturnIs,State}. %% Can ignore any value left on stack here. return_stmt(#return_stmt{exps=Es}, St0) -> {Ies,St1} = explist(Es, multiple, St0), {Ies ++ [?RETURN(length(Es))],St1}. %% block_stmt(Block, State) -> {BlockIs,State}. block_stmt(#block_stmt{body=Ss,lsz=Lsz,esz=Esz}, St0) -> {Iss,St1} = stmts(Ss, St0), {[?BLOCK_OPEN(Lsz, Esz)] ++ Iss ++ [?BLOCK_CLOSE],St1}. %% do_block(Block, Prefix, Postfix, State) -> {Block,State}. %% do_block(Block, State) -> {Block,State}. %% Do_block never returns external new variables. Fits into stmt(). do_block(Block, St) -> do_block(Block, [], [], St). do_block(#block{body=Ss,lsz=Lsz,esz=Esz}, Pre, Post, St0) -> {Iss,St1} = stmts(Ss, St0), {[?BLOCK_OPEN(Lsz, Esz)] ++ Pre ++ Iss ++ Post ++ [?BLOCK_CLOSE],St1}. %% while_stmt(While, State) -> {WhileIs,State}. while_stmt(#while_stmt{exp=E,body=B}, St0) -> {Ie,St1} = exp(E, single, St0), {Ib,St2} = do_block(B, St1), {[?WHILE(Ie, Ib)],St2}. %% repeat_stmt(Repeat, State) -> {RepeatIs,State}. repeat_stmt(#repeat_stmt{body=B}, St0) -> {Ib,St1} = do_block(B, St0), {[?REPEAT(Ib)],St1}. %% if_stmt(If, State) -> {IfIs,State}. %% We generate code which "steps down" the sequence of %% test-block. This means more nested calls but simpler emulator %% code. if_stmt(#if_stmt{tests=Ts,else_block=E}, St) -> if_tests(Ts, E, St). if_tests([{E,B}], #block{body=[]}, St0) -> Line = luerl_anno:line(element(2, E)), {CurLine,St1} = add_current_line(Line, St0), {Ie,St2} = exp(E, single, St1), {Ib,St3} = do_block(B, St2), {CurLine ++ Ie ++ [?IF_TRUE(Ib)],St3}; if_tests([{E,B}|Ts], Else, St0) -> Line = luerl_anno:line(element(2, E)), {CurLine,St1} = add_current_line(Line, St0), {Ie,St2} = exp(E, single, St1), {Ib,St3} = do_block(B, St2), {Its,St4} = if_tests(Ts, Else, St3), {CurLine ++ Ie ++ [?IF(Ib, Its)],St4}; if_tests([], Else, St0) -> {Ielse,St1} = do_block(Else, St0), {Ielse,St1}. %% numfor_stmt(For, State) -> {ForIs,State}. numfor_stmt(#nfor_stmt{var=V,init=I,limit=L,step=S,body=B}, St0) -> {Ies,St1} = explist([I,L,S], single, St0), {Ib,St2} = do_block(B, set_var(V), [], St1), {Ies ++ [?NFOR(V, Ib)],St2}. %% %% An experiment to put the block *outside* the for loop. %% numfor_stmt(#nfor_stmt{v=V,init=I,limit=L,step=S,b=B}, St0) -> %% {Ies,St1} = explist([I,L,S], single, St0), %% {Ib,St2} = do_block(B, St1), %% [?BLOCK(Lsz, Esz, Is)] = Ib, %% ForBlock = [?BLOCK(Lsz, Esz, [?NFOR(V,set_var(V) ++ Is)])], %% {Ies ++ ForBlock,St2}. %% genfor_stmt(For, State) -> {ForIs,State}. %% Evaluate the explist to return the generator function, data and %% initial value. The handling of setting the pushing and setting the %% vars in the block come from assign_local_loop. genfor_stmt(Gfor, St) -> genfor_stmt_1(Gfor, St). genfor_stmt_1(#gfor_stmt{vars=Vs,gens=Gs,body=B}, St0) -> {Igs,St1} = explist(Gs, multiple, St0), {Ias,St2} = assign_local_loop_var(Vs, 0, St1), {Ib,St3} = do_block(B, Ias, [], St2), {Igs ++ [?POP_VALS(length(Gs))] ++ [?GFOR(Vs, Ib)],St3}. %% local_assign_stmt(Local, State) -> {Ilocal,State}. %% We must evaluate all expressions, even the unneeded ones. %% Have two versions, run both and see that we get the same result. local_assign_stmt(#local_assign_stmt{vars=Vs,exps=Es}, St) -> R = assign_local_test(Vs, Es, St), R = assign_local(Vs, Es, St). assign_local([V|Vs], [], St0) -> {Ias,St1} = assign_local_loop_var(Vs, St0), {[?PUSH_LIT([])] ++ Ias ++ set_var(V),St1}; assign_local(Vs, Es, St) -> assign_local_loop(Vs, Es, St). assign_local_test([V|Vs], [], St0) -> {Ias,St1} = assign_loop_var(Vs, St0), {[?PUSH_LIT([])] ++ Ias ++ set_var(V),St1}; assign_local_test(Vs, Es, St) -> assign_loop(Vs, Es, St). %% assign_local_loop(Vars, Exps, State) -> {Iassigns,State}. %% Must be careful with pushing and popping values here. Make sure %% all non-last values are singleton. %% %% This could most likely be folded together with assign_loop/3. assign_local_loop([V], [E], St0) -> %Remove unnecessary ?PUSH_VALS {Ie,St1} = exp(E, single, St0), %Last argument to one variable! {Ie ++ set_var(V),St1}; assign_local_loop([V|Vs], [E], St0) -> {Ie,St1} = exp(E, multiple, St0), %Last argument to many vars! {Ias,St2} = assign_local_loop_var(Vs, St1), {Ie ++ Ias ++ set_var(V),St2}; assign_local_loop([V|Vs], [E|Es], St0) -> {Ie,St1} = exp(E, single, St0), %Not last argument! {Ias,St2} = assign_local_loop(Vs, Es, St1), {Ie ++ Ias ++ set_var(V),St2}; assign_local_loop([], Es, St) -> assign_local_loop_exp(Es, St). %% assign_local_loop_var(Vars, State) -> {Iassigns,State}. %% Extract necessary number of values from value list on stack. Pad %% with nil. assign_local_loop_var(Vs, St) -> assign_local_loop_var(Vs, 1, St). assign_local_loop_var([V|Vs], Vc, St0) -> {Ias,St1} = assign_local_loop_var(Vs, Vc+1, St0), {Ias ++ set_var(V),St1}; assign_local_loop_var([], Vc, St) -> {[?PUSH_VALS(Vc)],St}. %% assign_local_loop_exp(Exprs, State) -> {Instrs,State}. %% Evaluate remaining expressions and pop the values from the stack. assign_local_loop_exp([E|Es], St0) -> {Ie,St1} = exp(E, single, St0), %It will be dropped anyway {Ias,St2} = assign_local_loop_exp(Es, St1), {Ie ++ Ias ++ [?POP],St2}; %Pop value off stack assign_local_loop_exp([], St) -> {[],St}. %% local_fdef_stmt(Local, State) -> {ILocal,State}. local_fdef_stmt(#local_fdef_stmt{var=V,func=F}, St0) -> {If,St1} = functiondef(F, St0), {If ++ set_var(V),St1}. %% expr_stmt(Expr, State) -> {ExprIs,State}. %% The expression pseudo statement. This will return a single value %% which we leave on the stack. expr_stmt(#expr_stmt{exp=Exp}, St0) -> {Ie,St1} = exp(Exp, single, St0), {Ie,St1}. %% explist(Exprs, Values, State) -> {Instrs,State}. %% exp(Expr, Values, State) -> {Instrs,State}. %% Values determines if we are to only return the first value of a %% list of values. Values multiple makes us a return a list! explist([E], S, St) -> exp(E, S, St); %Append values to output? explist([E|Es], S, St0) -> {Ie,St1} = exp(E, single, St0), {Ies,St2} = explist(Es, S, St1), {Ie ++ Ies,St2}; explist([], _, St) -> {[],St}. %No expressions at all exp(#lit{val=L}, S, St) -> Is = [?PUSH_LIT(L)], {multiple_values(S, Is),St}; exp(#fdef{}=F, S, St0) -> {If,St1} = functiondef(F, St0), {multiple_values(S, If), St1}; exp(#op{op='and',args=[A1,A2]}, S, St0) -> {Ia1,St1} = exp(A1, S, St0), {Ia2,St2} = exp(A2, S, St1), {Ia1 ++ [?AND_THEN(Ia2)],St2}; %Must handle single/multiple exp(#op{op='or',args=[A1,A2]}, S, St0) -> {Ia1,St1} = exp(A1, S, St0), {Ia2,St2} = exp(A2, S, St1), {Ia1 ++ [?OR_ELSE(Ia2)],St2}; %Must handle single/multiple exp(#op{op=Op,args=As}, S, St0) -> {Ias,St1} = explist(As, single, St0), Iop = Ias ++ [?OP(Op,length(As))], {multiple_values(S, Iop),St1}; exp(#tabcon{fields=Fs}, S, St0) -> {Its,Fc,I,St1} = tableconstructor(Fs, St0), {Its ++ multiple_values(S, [?BUILD_TAB(Fc,I)]),St1}; exp(#lvar{n='...'}=V, S, St) -> %Can be either local or frame {single_value(S, get_var(V)),St}; exp(#evar{n='...'}=V, S, St) -> {single_value(S, get_var(V)),St}; exp(E, S, St) -> prefixexp(E, S, St). %% single_value(Values, Instrs) -> Instrs. %% multiple_values(Values, Instrs) -> Instrs. %% Ensure either single value or multiple value. single_value(single, Is) -> Is ++ [?SINGLE]; single_value(multiple, Is) -> Is. multiple_values(single, Is) -> Is; multiple_values(multiple, Is) -> Is ++ [?MULTIPLE]. %% prefixexp(Expr, Values, State) -> {Instrs,State}. %% prefixexp_rest(Expr, Values, State) -> {Instrs,State}. %% prefixexp_first(Expr, Values, State) -> {Instrs,State}. %% prefixexp_element(Expr, Values, State) -> {Instrs,State}. %% Single determines if we are to only return the first value of a %% list of values. Single false makes us a return a list! prefixexp(#dot{exp=Exp,rest=Rest}, S, St0) -> {Ie,St1} = prefixexp_first(Exp, single, St0), {Ir,St2} = prefixexp_rest(Rest, S, St1), {Ie ++ Ir,St2}; prefixexp(Exp, S, St) -> prefixexp_first(Exp, S, St). prefixexp_first(#single{exp=E}, S, St0) -> {Ie,St1} = exp(E, single, St0), %Will make it single {multiple_values(S, Ie),St1}; prefixexp_first(Var, S, St) -> {multiple_values(S, get_var(Var)),St}. prefixexp_rest(#dot{exp=Exp,rest=Rest}, S, St0) -> {Ie,St1} = prefixexp_element(Exp, single, St0), {Ir,St2} = prefixexp_rest(Rest, S, St1), {Ie ++ Ir,St2}; prefixexp_rest(Exp, S, St) -> prefixexp_element(Exp, S, St). prefixexp_element(#key{key=#lit{val=K}}, S, St) -> {multiple_values(S, [?GET_LIT_KEY(K)]),St}; prefixexp_element(#key{key=E}, S, St0) -> {Ie,St1} = exp(E, single, St0), {Ie ++ multiple_values(S, [?GET_KEY]),St1}; %% prefixexp_element(#fcall{args=[]}, S, St) -> %% Ifs = [?FCALL(0)], %% {single_value(S, Ifs),St}; %Function call returns list %% prefixexp_element(#fcall{args=As}, S, St0) -> %% {Ias,St1} = explist(As, multiple, St0), %% Ifs = Ias ++ [?FCALL(length(As))], %% {single_value(S, Ifs),St1}; %Function call returns list prefixexp_element(#fcall{args=As}, S, St0) -> {Ias,St1} = explist(As, multiple, St0), Ifs = Ias ++ [?POP_ARGS(length(As)),?FCALL], {single_value(S, Ifs),St1}; %Function call returns list %% prefixexp_element(#mcall{meth=#lit{val=K},args=[]}, S, St) -> %% Ims = [?MCALL(K, 0)], %% {single_value(S, Ims),St}; %Method call returns list %% prefixexp_element(#mcall{meth=#lit{val=K},args=As}, S, St0) -> %% {Ias,St1} = explist(As, multiple, St0), %% Ims = Ias ++ [?MCALL(K, length(As))], %% {single_value(S, Ims),St1}. %Method call returns list prefixexp_element(#mcall{meth=#lit{val=K},args=As}, S, St0) -> {Ias,St1} = explist(As, multiple, St0), Ims = Ias ++ [?POP_ARGS(length(As)),?MCALL(K)], {single_value(S, Ims),St1}. %Method call returns list %% functiondef(Func, State) -> {Func,State}. %% This will return a single value which we leave on the stack. Set %% the local current line to 0 to get correct line numbers inside the %% function. Reset to the original afterwards. functiondef(#fdef{l=Anno0,pars=Ps0,body=Ss,lsz=Lsz,esz=Esz}, #c_cg{line=Line,vfile=Vfile}=St0) -> St1 = St0#c_cg{line=0}, %Set current line to 0 %% Set the functions file annotation to the virtual file. Anno1 = luerl_anno:set(file, Vfile, Anno0), Ps1 = func_pars(Ps0), {Iss,St2} = stmts(Ss, St1), Iss1 = [?PUSH_ARGS(Ps1)] ++ gen_store(Ps1, Iss ++ [?RETURN(0)]), {[?PUSH_FDEF(Anno1,Lsz,Esz,Ps1,Iss1)],St2#c_cg{line=Line}}. func_pars([#evar{n='...',i=I}]) -> -I; %Tail is index for varargs func_pars([#lvar{n='...',i=I}]) -> I; func_pars([#evar{i=I}|Ps]) -> [-I|func_pars(Ps)]; func_pars([#lvar{i=I}|Ps]) -> [I|func_pars(Ps)]; func_pars([]) -> []. %No varargs %% Experiments testing case where we push the args onto the stack and %% have explicit instructions which pop them and store them in the LVs %% and Upvs. gen_store([V|Vs], Is) when V > 0 -> gen_store(Vs, [?STORE_LVAR(1, V)|Is]); gen_store([V|Vs], Is) when V < 0 -> gen_store(Vs, [?STORE_EVAR(1, -V)|Is]); gen_store([], Is) -> Is; gen_store(V, Is) when V > 0 -> [?STORE_LVAR(1, V)|Is]; gen_store(V, Is) when V < 0 -> [?STORE_LVAR(1, -V)|Is]. %% tableconstructor(Fields, State) -> {Ifields,FieldCount,Index,State}. %% FieldCount is how many Key/Value pairs are on the stack, Index is %% the index of the next value in the last value pushed. Make sure %% that the last value is a multiple. tableconstructor(Fs, St0) -> {Its,Fc,I,St1} = tc_fields(Fs, 0, St0), {Its,Fc,I,St1}. tc_fields([#efield{val=V}], I0, St0) -> I1 = I0 + 1, %Index of next element {Iv,St1} = exp(V, multiple, St0), {Iv,0,I1,St1}; tc_fields([#efield{val=V}|Fs], I0, St0) -> I1 = I0 + 1, %Index of next element {Iv,St1} = exp(V, single, St0), {Ifs,Fc,I2,St2} = tc_fields(Fs, I1, St1), {[?PUSH_LIT(I1)] ++ Iv ++ Ifs,Fc+1,I2,St2}; tc_fields([#kfield{key=K,val=V}|Fs], I0, St0) -> {Ik,St1} = exp(K, single, St0), {Iv,St2} = exp(V, single, St1), {Ifs,Fc,I1,St3} = tc_fields(Fs, I0, St2), {Ik ++ Iv ++ Ifs,Fc+1,I1,St3}; tc_fields([], _, St) -> {[?PUSH_LIT([])],0,1,St}. rvirding-luerl-772b25e/src/luerl_sup.erl0000664000232200023220000000357015114130670020676 0ustar debalancedebalance%% Copyright (c) 2013-2021 Robert Virding %% %% 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. -module(luerl_sup). -include("luerl.hrl"). ?MODULEDOC(false). -behaviour(supervisor). %% API -export([start_link/0]). %% Supervisor callbacks -export([init/1]). -define(SERVER, ?MODULE). %% =================================================================== %% API functions %% =================================================================== start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). %% =================================================================== %% Supervisor callbacks %% =================================================================== %% sup_flags() = #{strategy => strategy(), % optional %% intensity => non_neg_integer(), % optional %% period => pos_integer()} % optional %% child_spec() = #{id => child_id(), % mandatory %% start => mfargs(), % mandatory %% restart => restart(), % optional %% shutdown => shutdown(), % optional %% type => worker(), % optional %% modules => modules()} % optional init([]) -> SupFlags = #{strategy => one_for_one, intensity => 5, period => 10}, ChildSpecs = [], {ok, {SupFlags, ChildSpecs}}. %% internal functions rvirding-luerl-772b25e/src/luerl_heap.erl0000664000232200023220000006721315114130670021010 0ustar debalancedebalance%% Copyright (c) 2020-2024 Robert Virding %% %% 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. %% File : luerl_heap.erl %% Author : Robert Virding %% Purpose : Implements the heap section of the Luerl state. %% %% Note that here we only handle the data in the heap and never call %% anything in either Luerl or Erlang. Those cases where this could %% happen we return values informing the caller to do it. For example %% in the set_table_key and get_table_key functions. -module(luerl_heap). -include("luerl.hrl"). ?MODULEDOC(false). -export([init/0]). %% External interface. -export([gc/1, alloc_table/1,alloc_table/2,free_table/2, get_table/2,set_table/3,upd_table/3,chk_table/2, get_global_key/2,set_global_key/3, get_table_key/3,set_table_key/4, raw_get_table_key/3,raw_set_table_key/4, alloc_userdata/2,alloc_userdata/3,free_userdata/2, get_userdata/2,set_userdata/3,upd_userdata/3,chk_userdata/2, set_userdata_data/3,get_userdata_data/2, alloc_funcdef/2,get_funcdef/2,set_funcdef/3, alloc_environment/2,get_env_var/3,set_env_var/4, get_metamethod/3,get_metamethod/4, get_metatable/2, set_metatable/3 ]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %% init() -> State %% %% Initialise the heap section of the state and return the state. init() -> St0 = #luerl{meta=#meta{},tag=make_ref()}, init_tables(St0). init_tables(St) -> %% Initialise the table handling. Tst = init_tstruct(), %% Initialise the environment handling. Est = init_tstruct(), %% Initialise the userdata handling. Ust = init_tstruct(), %% Initialise the function def handling. Fst = init_tstruct(), St#luerl{tabs=Tst,envs=Est,usds=Ust,fncs=Fst}. %% init_tstruct() -> #tstruct{}. %% alloc_tstruct(Val, #tstruct{}) -> {Index,#tstruct{}}. %% set_tstruct(Index, Val, #tstruct{}) -> #tstruct{}. %% upd_tstruct(Index, UpdFun, #tstruct{}) -> #tstruct{}. %% del_tstruct(Index, #tstruct{}) -> #tstruct{}. %% chk_tstruct(Index, #tstruct{}) -> ok | error. %% %% Functions for accessing tstructs. init_tstruct() -> #tstruct{data=?MAKE_TABLE(),free=[],next=0}. alloc_tstruct(Val, #tstruct{data=D0,free=[N|Ns]}=Tstr) -> D1 = ?SET_TABLE(N, Val, D0), {N,Tstr#tstruct{data=D1,free=Ns}}; alloc_tstruct(Val, #tstruct{data=D0,free=[],next=N}=Tstr) -> D1 = ?SET_TABLE(N, Val, D0), {N,Tstr#tstruct{data=D1,next=N+1}}. set_tstruct(N, Val, #tstruct{data=D0}=Tstr) -> D1 = ?SET_TABLE(N, Val, D0), Tstr#tstruct{data=D1}. upd_tstruct(N, Upd, #tstruct{data=D0}=Tstr) -> D1 = ?UPD_TABLE(N, Upd, D0), Tstr#tstruct{data=D1}. del_tstruct(N, #tstruct{data=D0,free=Ns}=Tstr) -> D1 = ?DEL_TABLE(N, D0), Tstr#tstruct{data=D1,free=[N|Ns]}. -compile({inline,[get_tstruct/2]}). %Such a simple function get_tstruct(N, Tstr) -> ?GET_TABLE(N, Tstr#tstruct.data). chk_tstruct(N, Tstr) -> case ?CHK_TABLE(N, Tstr#tstruct.data) of true -> ok; false -> error end. %% alloc_table(State) -> {Tref,State} %% %% Allocate an empty table. alloc_table(St) -> alloc_table([], St). %% alloc_table(InitialTable, State) -> {Tref,State} %% %% The InitialTable is [{Key,Value}] or map, there is no longer any %% need to have it as an orddict. alloc_table(Itab, #luerl{tabs=Tst0}=St) -> Tab = create_table(Itab), {N,Tst1} = alloc_tstruct(Tab, Tst0), {#tref{i=N},St#luerl{tabs=Tst1}}. create_table(Itab) when is_map(Itab) -> create_table(maps:to_list(Itab)); create_table(Itab) -> D0 = ttdict:new(), A0 = array:new([{default,nil}]), %Arrays with 'nil' as default Init = fun ({_,nil}, {D,A}) -> {D,A}; %Ignore nil values ({K,V}, {D,A}) when is_integer(K), K >= 1 -> {D,array:set(K, V, A)}; ({K,V}, {D,A}) when is_float(K) -> case ?IS_FLOAT_INT(K, I) of true when I >= 1 -> {D,array:set(I, V, A)}; _NegFalse -> {ttdict:store(K, V, D),A} end; ({K,V}, {D,A}) -> {ttdict:store(K, V, D),A} end, {D1,A1} = lists:foldl(Init, {D0,A0}, Itab), #table{a=A1,d=D1,meta=nil}. %% free_table(Tref, State) -> State %% %% Delete a table freeing its space. free_table(#tref{i=N}, #luerl{tabs=Tst0}=St) -> Tst1 = del_tstruct(N, Tst0), St#luerl{tabs=Tst1}. %% get_table(Tref, State) -> Table %% %% Get the table referred to by Tref. get_table(#tref{i=N}, #luerl{tabs=Tst}) -> get_tstruct(N, Tst). %% set_table(Tref, Table, State) -> State %% %% Set a new table at the location referred to by Tref %% overwriting the existing one. set_table(#tref{i=N}, Tab, #luerl{tabs=Tst0}=St) -> Tst1 = set_tstruct(N, Tab, Tst0), St#luerl{tabs=Tst1}. %% upd_table(Tref, Fun, State) -> State %% %% Update the table at the location referred to by Tref. upd_table(#tref{i=N}, Upd, #luerl{tabs=Tst0}=St) -> Tst1 = upd_tstruct(N, Upd, Tst0), St#luerl{tabs=Tst1}. %% chk_table(Tref, State) -> ok | error. %% %% Check the table referenced by Tref actually exists. chk_table(#tref{i=N}, #luerl{tabs=Tst}) -> chk_tstruct(N, Tst). %% set_global_key(Key, Value, State) -> %% {value,Value,State} | {meta,Method,Args,State} | {error,Error,State} %% %% Set a key in the global name table _G to value. set_global_key(Key, Val, #luerl{g=G}=St) -> set_table_key(G, Key, Val, St). %% get_global_key(Key, State) -> %% {value,Value,State} | {meta,Method,Args,State} | {error,Error,State} %% %% Get the value of a key in the global name table, _G. get_global_key(Key, #luerl{g=G}=St) -> get_table_key(G, Key, St). %% set_table_key(Table, Key, Val, State) -> %% {value,Value,State} | {meta,Method,Args,State} | {error,Error,State} %% %% We don't make calls to meta methods or generate errors but %% return a value indicating this. Setting a value to 'nil' will clear %% it from the table and the array. We won't add a nil value. set_table_key(#tref{}=Tref, Key, Val, St) when is_integer(Key), Key >= 1 -> set_table_key_int(Tref, Key, Key, Val, St); set_table_key(#tref{}=Tref, Key, Val, St) when is_float(Key) -> case ?IS_FLOAT_INT(Key, I) of true when I >= 1 -> set_table_key_int(Tref, Key, I, Val, St); _NegFalse -> set_table_key_key(Tref, Key, Val, St) end; set_table_key(Tab, nil=Key, _, St) -> {error,{illegal_index,Tab,Key},St}; set_table_key(#tref{}=Tref, Key, Val, St) -> set_table_key_key(Tref, Key, Val, St); set_table_key(Other, Key, Val, St) -> Meta = get_metamethod(Other, <<"__newindex">>, St), %% io:format("stk ~p ~p ~p -> ~p\n", [Other,Key,Val,aMeta]), case Meta of nil -> {error,{illegal_index,Other,Key},St}; Meth when ?IS_FUNCTION(Meth) -> {meta,Meth,[Other,Key,Val],St}; Meth -> %Recurse down the metatable set_table_key(Meth, Key, Val, St) end. set_table_key_key(#tref{i=N}=Tab, Key, Val, #luerl{tabs=Tst0}=St) -> Ts0 = Tst0#tstruct.data, #table{d=Dict0,meta=Meta}=T = ?GET_TABLE(N, Ts0), case ttdict:find(Key, Dict0) of {ok,_} -> %Key exists Dict1 = if Val =:= nil -> ttdict:erase(Key, Dict0); true -> ttdict:store(Key, Val, Dict0) end, Ts1 = ?SET_TABLE(N, T#table{d=Dict1}, Ts0), Tst1 = Tst0#tstruct{data=Ts1}, {value,[],St#luerl{tabs=Tst1}}; error -> %Key does not exist case get_metamethod_tab(Meta, <<"__newindex">>, Ts0) of nil -> %% Only add non-nil value. Dict1 = if Val =:= nil -> Dict0; true -> ttdict:store(Key, Val, Dict0) end, Ts1 = ?SET_TABLE(N, T#table{d=Dict1}, Ts0), Tst1 = Tst0#tstruct{data=Ts1}, {value,[],St#luerl{tabs=Tst1}}; Meth when ?IS_FUNCTION(Meth) -> {meta,Meth,[Tab,Key,Val],St}; Meth -> set_table_key(Meth, Key, Val, St) end end. set_table_key_int(#tref{i=N}=Tab, Key, I, Val, #luerl{tabs=Tst0}=St) -> Ts0 = Tst0#tstruct.data, #table{a=Arr0,meta=Meta}=T = ?GET_TABLE(N, Ts0), case array:get(I, Arr0) of nil -> %Key does not exist case get_metamethod_tab(Meta, <<"__newindex">>, Ts0) of nil -> %% Only add non-nil value, slightly faster (?) Arr1 = if Val =:= nil -> Arr0; true -> array:set(I, Val, Arr0) end, Ts1 = ?SET_TABLE(N, T#table{a=Arr1}, Ts0), Tst1 = Tst0#tstruct{data=Ts1}, {value,[],St#luerl{tabs=Tst1}}; Meth when ?IS_FUNCTION(Meth) -> {meta,Meth,[Tab,Key,Val],St}; Meth -> set_table_key(Meth, Key, Val, St) end; _ -> %Key exists %% Can do this as 'nil' is default value of array. Arr1 = array:set(I, Val, Arr0), Ts1 = ?SET_TABLE(N, T#table{a=Arr1}, Ts0), Tst1 = Tst0#tstruct{data=Ts1}, {value,[],St#luerl{tabs=Tst1}} end. %% get_table_key(Table, Key, State) -> %% {value,Value,State} | {meta,Method,Args,State} | {error,Error,State} %% %% We don't make calls to meta methods or generate errors but %% return value indicating this. get_table_key(#tref{}=Tref, Key, St) when is_integer(Key), Key >= 1 -> get_table_key_int(Tref, Key, Key, St); get_table_key(#tref{}=Tref, Key, St) when is_float(Key) -> case ?IS_FLOAT_INT(Key, I) of true when I >= 1 -> get_table_key_int(Tref, Key, I, St); _NegFalse -> get_table_key_key(Tref, Key, St) end; get_table_key(#tref{}=Tref, Key, St) -> get_table_key_key(Tref, Key, St); get_table_key(Other, Key, St) -> %Just find the metamethod Meta = get_metamethod(Other, <<"__index">>, St), %% io:format("gtk ~p ~p -> ~p\n", [Other,Key,Meta]), case Meta of nil -> {error,{illegal_index,Other,Key},St}; Meth when ?IS_FUNCTION(Meth) -> {meta,Meth,[Other,Key],St}; Meth -> %Recurse down the metatable get_table_key(Meth, Key, St) end. get_table_key_key(#tref{i=N}=Tab, Key, #luerl{tabs=#tstruct{data=Ts}}=St) -> #table{d=Dict,meta=Meta} = ?GET_TABLE(N, Ts), case ttdict:find(Key, Dict) of {ok,Val} -> {value,Val,St}; error -> %% Key not present so try metamethod get_table_key_metamethod(Tab, Meta, Key, Ts, St) end. get_table_key_int(#tref{i=N}=T, Key, I, #luerl{tabs=#tstruct{data=Ts}}=St) -> #table{a=A,meta=Meta} = ?GET_TABLE(N, Ts), %Get the table. case array:get(I, A) of nil -> %% Key not present so try metamethod get_table_key_metamethod(T, Meta, Key, Ts, St); Val -> {value,Val,St} end. get_table_key_metamethod(Tab, Meta, Key, Ts, St) -> case get_metamethod_tab(Meta, <<"__index">>, Ts) of nil -> {value,nil,St}; Meth when ?IS_FUNCTION(Meth) -> {meta,Meth,[Tab,Key],St}; Meth -> %Recurse down the metatable get_table_key(Meth, Key, St) end. %% raw_get_table_key(Table, Key, State) -> Value. %% raw_set_table_key(Table, Key, Value, State) -> State. %% %% Get/set key values in tables without metamethods. raw_get_table_key(#tref{i=N}, Key, #luerl{tabs=Tst}) when is_integer(Key), Key >= 1 -> raw_get_table_key_int(N, Key, Tst); raw_get_table_key(#tref{i=N}, Key, #luerl{tabs=Tst}) when is_float(Key) -> case ?IS_FLOAT_INT(Key, I) of true when I >= 1 -> raw_get_table_key_int(N, I, Tst); _NegFalse -> raw_get_table_key_key(N, Key, Tst) end; raw_get_table_key(#tref{i=N}, Key, #luerl{tabs=Tst}) -> raw_get_table_key_key(N, Key, Tst). raw_get_table_key_key(N, Key, Tst) -> #table{d=Dict} = get_tstruct(N, Tst), case ttdict:find(Key, Dict) of {ok,Val} -> Val; error -> nil end. raw_get_table_key_int(N, Key, Tst) -> #table{a=Arr} = get_tstruct(N, Tst), array:get(Key, Arr). raw_set_table_key(#tref{}=Tref, Key, Val, #luerl{tabs=Tst0}=St) when is_integer(Key), Key >= 1 -> Tst1 = raw_set_table_key_int(Tref, Key, Val, Tst0), St#luerl{tabs=Tst1}; raw_set_table_key(#tref{}=Tref, Key, Val, #luerl{tabs=Tst0}=St) when is_float(Key) -> Tst1 = case ?IS_FLOAT_INT(Key, I) of true when I >= 1 -> raw_set_table_key_int(Tref, I, Val, Tst0); _NegFalse -> raw_set_table_key_key(Tref, Key, Val, Tst0) end, St#luerl{tabs=Tst1}; raw_set_table_key(#tref{}=Tref, Key, Val, #luerl{tabs=Tst0}=St) -> Tst1 = raw_set_table_key_key(Tref, Key, Val, Tst0), St#luerl{tabs=Tst1}. raw_set_table_key_key(#tref{i=N}, Key, Val, Tst0) -> Fun = fun (#table{d=Dict0}=Tab) -> Dict1 = if Val =:= nil -> ttdict:erase(Key, Dict0); true -> ttdict:store(Key, Val, Dict0) end, Tab#table{d=Dict1} end, upd_tstruct(N, Fun, Tst0). raw_set_table_key_int(#tref{i=N}, Key, Val, Tst0) -> Fun = fun (#table{a=Arr0}=Tab) -> %% Default array value is nil. Arr1 = array:set(Key, Val, Arr0), Tab#table{a=Arr1} end, upd_tstruct(N, Fun, Tst0). %% alloc_userdata(Data, State) -> {Usdref,State} %% %% Allocate userdata with empty metadata. alloc_userdata(Data, St) -> alloc_userdata(Data, nil, St). %% alloc_userdata(Data, Meta, State) -> {Usdref,State}. %% %% Allocate userdata setting its metadata. alloc_userdata(Data, Meta, #luerl{usds=Ust0}=St) -> Ud = #userdata{d=Data,meta=Meta}, {N,Ust1} = alloc_tstruct(Ud, Ust0), {#usdref{i=N},St#luerl{usds=Ust1}}. %% free_userdata(Usdref, State) -> State %% %% Delete a table freeing its space. free_userdata(#usdref{i=N}, #luerl{usds=Ust0}=St) -> Ust1 = del_tstruct(N, Ust0), St#luerl{usds=Ust1}. %% get_userdata(Usdref, State) -> {UserData,State} %% %% Get the userdata refered to by Usdref, get_userdata(#usdref{i=N}, #luerl{usds=Ust}=St) -> #userdata{} = Udata = get_tstruct(N, Ust), {Udata,St}. %% set_userdata(Usdref, Data, State) -> State %% %% Set a new userdata at the location referred to by Usdref %% overwriting the existing one. set_userdata(#usdref{i=N}, #userdata{}=Udata, #luerl{usds=Ust0}=St) -> Ust1 = set_tstruct(N, Udata, Ust0), St#luerl{usds=Ust1}. %% upd_userdata(Usdref, Fun, State) -> State %% %% Update the data in the userdata referred to by Usdref. upd_userdata(#usdref{i=N}, Upd, #luerl{usds=Ust0}=St) -> Ust1 = upd_tstruct(N, Upd, Ust0), St#luerl{usds=Ust1}. %% chk_userdata(Usdref, State) -> ok | error. %% %% Check the userdata referenced by Tref actually exists. chk_userdata(#usdref{i=N}, #luerl{usds=Ust}) -> chk_tstruct(N, Ust). %% get_userdata_data(Usdref, State) -> {Data,State} %% %% Get the data form the userdata refered to by Usdref. get_userdata_data(#usdref{i=N}, #luerl{usds=Ust}=St) -> Udata = get_tstruct(N, Ust), {Udata#userdata.d,St}. %% set_userdata_data(Usdref, Data, State) -> State %% %% Set a new userdata at the location referred to by Usdref %% overwriting the existing one. set_userdata_data(#usdref{i=N}, Data, #luerl{usds=Ust0}=St) -> Ust1 = upd_tstruct(N, fun (Ud) -> Ud#userdata{d=Data} end, Ust0), St#luerl{usds=Ust1}. %% make_userdata(Data) -> make_userdata(Data, nil). %% make_userdata(Data, Meta) -> #userdata{d=Data,meta=Meta}. %% alloc_funcdef(Def, State) -> {FunRef,State} alloc_funcdef(Func, #luerl{fncs=Fst0}=St) -> {N,Fst1} = alloc_tstruct(Func, Fst0), {#funref{i=N},St#luerl{fncs=Fst1}}. %% get_funcdef(Funref, State) -> {Fdef,State} %% %% Get the function data referred to by Fref. get_funcdef(#funref{i=N}, #luerl{fncs=Fst}=St) -> Fdef = get_tstruct(N, Fst), {Fdef,St}. %% set_funcdef(Funref, Fdef, State) -> State. %% %% Set the function data referred to by Fref. set_funcdef(#funref{i=N}, Func, #luerl{fncs=Fst0}=St) -> Fst1 = set_tstruct(N, Func, Fst0), St#luerl{fncs=Fst1}. %% get_metamethod(Object, Event, State) -> Method | nil %% get_metamethod(Object1, Object2, Event, State) -> Method | nil %% %% Get the metamethod for object(s). get_metamethod(O1, O2, E, St) -> case get_metamethod(O1, E, St) of nil -> get_metamethod(O2, E, St); M -> M end. get_metamethod(O, E, St) -> Meta = get_metatable(O, St), %Can be nil %% io:format("gm ~p ~p -> ~p\n", [O,E,Meta]), get_metamethod_tab(Meta, E, St#luerl.tabs#tstruct.data). get_metamethod_tab(#tref{i=M}, E, Ts) -> #table{d=Mdict} = ?GET_TABLE(M, Ts), case ttdict:find(E, Mdict) of {ok,Mm} -> Mm; error -> nil end; get_metamethod_tab(_, _, _) -> nil. %Other types have no metatables %% get_metatable(Obj, State) -> MetaTable %% %% Get the metatable of an object or its type metatable. get_metatable(#tref{i=T}, #luerl{tabs=Tst}) -> (get_tstruct(T, Tst))#table.meta; get_metatable(#usdref{i=U}, #luerl{usds=Ust}) -> (get_tstruct(U, Ust))#userdata.meta; get_metatable(nil, #luerl{meta=Meta}) -> Meta#meta.nil; get_metatable(B, #luerl{meta=Meta}) when is_boolean(B) -> Meta#meta.boolean; get_metatable(N, #luerl{meta=Meta}) when is_number(N) -> Meta#meta.number; get_metatable(S, #luerl{meta=Meta}) when is_binary(S) -> Meta#meta.string; get_metatable(_, _) -> nil. %Other types have no metatables %% set_metatable(Obj, MetaTable, State) -> State %% %% Set the metatable of an object or its type metatable. set_metatable(#tref{i=N}, M, #luerl{tabs=Tst0}=St) -> Tst1 = upd_tstruct(N, fun (Tab) -> Tab#table{meta=M} end, Tst0), St#luerl{tabs=Tst1}; set_metatable(#usdref{i=N}, M, #luerl{usds=Ust0}=St) -> Ust1 = upd_tstruct(N, fun (Ud) -> Ud#userdata{meta=M} end, Ust0), St#luerl{usds=Ust1}; set_metatable(nil, M, #luerl{meta=Meta0}=St) -> Meta1 = Meta0#meta{nil=M}, St#luerl{meta=Meta1}; set_metatable(B, M, #luerl{meta=Meta0}=St) when is_boolean(B) -> Meta1 = Meta0#meta{boolean=M}, St#luerl{meta=Meta1}; set_metatable(N, M, #luerl{meta=Meta0}=St) when is_number(N) -> Meta1 = Meta0#meta{number=M}, St#luerl{meta=Meta1}; set_metatable(B, M, #luerl{meta=Meta0}=St) when is_binary(B) -> Meta1 = Meta0#meta{string=M}, St#luerl{meta=Meta1}; set_metatable(_, _, St) -> %Do nothing for the rest St. %% alloc_environment(Size, State) -> {Fref,State} %% %% Allocate the environment in the environment table and return %% its eref. alloc_environment(Size, #luerl{envs=Est0}=St) -> Fr = erlang:make_tuple(Size, nil), {N,Est1} = alloc_tstruct(Fr, Est0), {#eref{i=N},St#luerl{envs=Est1}}. %% get_env_var(Eref, Index, State) -> Value. %% set_env_var(Eref, Index, Val, State) -> State. get_env_var(#eref{i=N}, Index, #luerl{envs=Etab}) -> element(Index, get_tstruct(N, Etab)). %% element(Index, ?GET_TABLE(N, Etab#tstruct.data)). set_env_var(#eref{i=N}, Index, Val, #luerl{envs=Est0}=St) -> Est1 = upd_tstruct(N, fun (Fr) -> setelement(Index, Fr, Val) end, Est0), St#luerl{envs=Est1}. %% gc(State) -> State. %% The garbage collector. Its main job is to reclaim unused tables %% and frames. It is a mark/sweep collector which passes over all %% objects and marks tables and frames which it has seen. All unseen %% tables and frames are then freed and their indexes added to the %% free lists. -record(gct, {t,s}). %Gc table info table, seen gc(#luerl{tabs=#tstruct{data=Tt0,free=Tf0}=Tab0, envs=#tstruct{data=Et0,free=Ef0}=Env0, usds=#tstruct{data=Ut0,free=Uf0}=Usd0, fncs=#tstruct{data=Ft0,free=Ff0}=Fnc0, g=G,stk=Stk,cs=Cs,meta=Meta}=St) -> %% The root set consisting of global table and stack. Root = [Meta#meta.nil,Meta#meta.boolean,Meta#meta.number,Meta#meta.string, G|Stk], %% Mark all seen tables and frames, i.e. return them. GcT = #gct{t=Tt0,s=[]}, GcE = #gct{t=Et0,s=[]}, GcU = #gct{t=Ut0,s=[]}, GcF = #gct{t=Ft0,s=[]}, {SeenT,SeenE,SeenU,SeenF} = mark(Root, [Cs], GcT, GcE, GcU, GcF), %% io:format("gc: ~p\n", [{SeenT,SeenF,SeenU}]), %% Free unseen tables and add freed to free list. {Tf1,Tt1} = filter_tables(SeenT, Tf0, Tt0), {Ef1,Et1} = filter_environment(SeenE, Ef0, Et0), {Uf1,Ut1} = filter_userdata(SeenU, Uf0, Ut0), {Ff1,Ft1} = filter_funcdefs(SeenF, Ff0, Ft0), %% And update the tables. Tab1 = Tab0#tstruct{data=Tt1,free=Tf1}, Env1 = Env0#tstruct{data=Et1,free=Ef1}, Usd1 = Usd0#tstruct{data=Ut1,free=Uf1}, Fnc1 = Fnc0#tstruct{data=Ft1,free=Ff1}, St#luerl{tabs=Tab1,envs=Env1,usds=Usd1,fncs=Fnc1}. %% mark(ToDo, MoreTodo, GcTabs, GcEnv, GcUserdata, GcFuncdefs) -> %% {SeenTabs,SeenFrames,SeenUserdata,SeenFuncdefs}. %% Scan over all live objects and mark seen tables by adding them to %% the seen list. mark([{in_table,_}=_T|Todo], More, GcT, GcE, GcU, GcF) -> %%io:format("gc: ~p\n", [_T]), mark(Todo, More, GcT, GcE, GcU, GcF); mark([#tref{i=T}|Todo], More, #gct{t=Tt,s=Ts0}=GcT, GcE, GcU, GcF) -> case ordsets:is_element(T, Ts0) of true -> %Already done mark(Todo, More, GcT, GcE, GcU, GcF); false -> %Mark it and add to todo Ts1 = ordsets:add_element(T, Ts0), #table{a=Arr,d=Dict,meta=Meta} = ?GET_TABLE(T, Tt), %% Have to be careful when adding Tab and Meta as Tab is %% [{Key,Val}], Arr is array and Meta is %% nil|#tref{i=M}. We want lists. Aes = array:sparse_to_list(Arr), Des = ttdict:to_list(Dict), mark([Meta|Todo], [[{in_table,T}],Des,Aes,[{in_table,-T}]|More], GcT#gct{s=Ts1}, GcE, GcU, GcF) end; mark([#eref{i=F}|Todo], More, GcT, #gct{t=Et,s=Es0}=GcE, GcU, GcF) -> %% io:format("eref0: ~p\ ~p ~pn", [F,Et,Es0]), case ordsets:is_element(F, Es0) of true -> %Already done mark(Todo, More, GcT, GcE, GcU, GcF); false -> %Mark it and add to todo Es1 = ordsets:add_element(F, Es0), Ses = tuple_to_list(?GET_TABLE(F, Et)), %% io:format("eref1: ~p ~p\n", [Et,Es1]), mark(Todo, [Ses|More], GcT, GcE#gct{s=Es1}, GcU, GcF) end; mark([#usdref{i=U}|Todo], More, GcT, GcE, #gct{s=Us0}=GcU, GcF) -> case ordsets:is_element(U, Us0) of true -> %Already done mark(Todo, More, GcT, GcE, GcU, GcF); false -> Us1 = ordsets:add_element(U, Us0), mark(Todo, More, GcT, GcE, GcU#gct{s=Us1}, GcF) end; mark([#funref{i=F,env=Erefs}|ToDo], More, GcT, GcE, GcU, #gct{t=Ft0,s=Fs0}=GcF) -> %% io:format("funref0: ~p ~p ~p\n", [F,Fs0,Erefs]), %% Each funref has its own environments but we only need to add %% the function definition once. case ordsets:is_element(F, Fs0) of true -> mark(ToDo, [Erefs|More], GcT, GcE, GcU, GcF); false -> %% And mark the function definition. Fs1 = ordsets:add_element(F, Fs0), Fdef = ?GET_TABLE(F, Ft0), %% io:format("funref1: ~p ~p ~p\n", [F,Fs1,Erefs]), mark([Fdef|ToDo], [Erefs|More], GcT, GcE, GcU, GcF#gct{s=Fs1}) end; mark([#lua_func{funrefs=Funrefs}|Todo], More, GcT, GcE, GcU, GcF) -> %% io:format("push funrefs ~p\n", [Funrefs]), mark(Todo, [Funrefs|More], GcT, GcE, GcU, GcF); %% The call stack. mark([#call_frame{func=Funref,lvs=Lvs,env=Env}|Todo], More0, GcT, GcE, GcU, GcF) -> %% io:format("cf ~p\n", [Funref]), More1 = [ tuple_to_list(Lv) || Lv <- Lvs, is_tuple(Lv) ] ++ [Env|More0], mark([Funref|Todo], More1, GcT, GcE, GcU, GcF); mark([#loop_frame{lvs=Lvs,stk=Stk,env=Env}|Todo], More0, GcT, GcE, GcU, GcF) -> More1 = [ tuple_to_list(Lv) || Lv <- Lvs, is_tuple(Lv) ] ++ [Stk,Env|More0], mark(Todo, More1, GcT, GcE, GcU, GcF); %% Specifically catch these as they would match table key-value pair. mark([#erl_func{}|Todo], More, GcT, GcE, GcU, GcF) -> mark(Todo, More, GcT, GcE, GcU, GcF); mark([#erl_mfa{}|Todo], More, GcT, GcE, GcU, GcF) -> mark(Todo, More, GcT, GcE, GcU, GcF); mark([#thread{}|Todo], More, GcT, GcE, GcU, GcF) -> mark(Todo, More, GcT, GcE, GcU, GcF); mark([#userdata{meta=Meta}|Todo], More, GcT, GcE, GcU, GcF) -> mark([Meta|Todo], More, GcT, GcE, GcU, GcF); mark([{K,V}|Todo], More, GcT, GcE, GcU, GcF) -> %Table key-value pair %% io:format("mt: ~p\n", [{K,V}]), mark([K,V|Todo], More, GcT, GcE, GcU, GcF); mark([_|Todo], More, GcT, GcE, GcU, GcF) -> %% Can ignore everything else. mark(Todo, More, GcT, GcE, GcU, GcF); mark([], [M|More], GcT, GcE, GcU, GcF) -> mark(M, More, GcT, GcE, GcU, GcF); mark([], [], #gct{s=St}, #gct{s=Se}, #gct{s=Su}, #gct{s=Sf}) -> {St,Se,Su,Sf}. %% filter_tables(Seen, Free, Tables) -> {Free,Tables}. %% filter_environment(Seen, Free, Frames) -> {Free,Frames}. %% filter_userdata(Seen, Free, Frames) -> {Free,Frames}. %% filter_funcdefs(Seen, Free, Frames) -> {Free,Frames}. %% Filter tables/frames/userdata/funcdefs and return updated free %% lists and tables/frames. filter_tables(Seen, Tf0, Tt0) -> %% Update the free list. Tf1 = ?FOLD_TABLES(fun (K, _, Free) -> case ordsets:is_element(K, Seen) of true -> Free; false -> [K|Free] end end, Tf0, Tt0), Tt1 = ?FILTER_TABLES(fun (K, _) -> ordsets:is_element(K, Seen) end, Tt0), {Tf1,Tt1}. filter_environment(Seen, Ef0, Et0) -> %% io:format("env0: ~p ~p ~p\n", [Seen,Ef0,Et0]), %% Update the free list. Ef1 = ?FOLD_TABLES(fun (K, _, Free) -> case ordsets:is_element(K, Seen) of true -> Free; false -> [K|Free] end end, Ef0, Et0), Et1 = ?FILTER_TABLES(fun (K, _) -> ordsets:is_element(K, Seen) end, Et0), %% io:format("env1: ~p ~p\n", [Ef1,Et1]), {Ef1,Et1}. filter_userdata(Seen, Uf0, Ut0) -> %% Update the free list. Uf1 = ?FOLD_TABLES(fun (K, _, Free) -> case ordsets:is_element(K, Seen) of true -> Free; false -> [K|Free] end end, Uf0, Ut0), %% Reclaim free table slots. Ut1 = ?FILTER_TABLES(fun (K, _) -> ordsets:is_element(K, Seen) end, Ut0), {Uf1,Ut1}. filter_funcdefs(Seen, Ff0, Ft0) -> %% Update the free list. Ff1 = ?FOLD_TABLES(fun (K, _, Free) -> case ordsets:is_element(K, Seen) of true -> Free; false -> [K|Free] end end, Ff0, Ft0), Ft1 = ?FILTER_TABLES(fun (K, _) -> ordsets:is_element(K, Seen) end, Ft0), {Ff1,Ft1}. rvirding-luerl-772b25e/src/luerl_lib_string.erl0000664000232200023220000006152415114130670022226 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl_lib_string.erl %% Author : Robert Virding %% Purpose : The string library for Luerl. -module(luerl_lib_string). -include("luerl.hrl"). ?MODULEDOC(false). %% The basic entry point to set up the function table. -export([install/1,byte/3,char/3,dump/3,find/3,format/3,gmatch/3,gsub/3,len/3,lower/3, match/3,rep/3,reverse/3,sub/3,upper/3]). %% Export some test functions. -export([test_gsub/3,test_match_pat/3,test_pat/1, test_byte/3,test_do_find/4,test_sub/2,test_sub/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this %%-compile([bin_opt_info]). %For when we are optimising install(St0) -> {T,St1} = luerl_heap:alloc_table(table(), St0), {M,St2} = luerl_heap:alloc_table(metatable(T), St1), Meta0 = St2#luerl.meta, Meta1 = Meta0#meta{string=M}, {T,St2#luerl{meta=Meta1}}. %% metatable(Table) -> [{TableName,Table}]. %% table() -> [{FuncName,Function}]. metatable(T) -> %String type metatable [{<<"__index">>,T}]. table() -> %String table [{<<"byte">>,#erl_mfa{m=?MODULE,f=byte}}, {<<"char">>,#erl_mfa{m=?MODULE,f=char}}, {<<"dump">>,#erl_mfa{m=?MODULE,f=dump}}, {<<"find">>,#erl_mfa{m=?MODULE,f=find}}, {<<"format">>,#erl_mfa{m=?MODULE,f=format}}, {<<"gmatch">>,#erl_mfa{m=?MODULE,f=gmatch}}, {<<"gsub">>,#erl_mfa{m=?MODULE,f=gsub}}, {<<"len">>,#erl_mfa{m=?MODULE,f=len}}, {<<"lower">>,#erl_mfa{m=?MODULE,f=lower}}, {<<"match">>,#erl_mfa{m=?MODULE,f=match}}, {<<"rep">>,#erl_mfa{m=?MODULE,f=rep}}, {<<"reverse">>,#erl_mfa{m=?MODULE,f=reverse}}, {<<"sub">>,#erl_mfa{m=?MODULE,f=sub}}, {<<"upper">>,#erl_mfa{m=?MODULE,f=upper}} ]. %% byte(String [, I [, J]] ) -> [Code] %% Return numerical codes of string between I and J. byte(_, As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_integer,lua_integer]) of [S|Is] -> Bs = do_byte(S, byte_size(S), Is), {Bs,St}; _ -> badarg_error(byte, As, St) %nil or [] end. test_byte(S, I, J) -> do_byte(S, byte_size(S), I, J). do_byte(_, 0, _) -> [nil]; do_byte(S, Len, []) -> do_byte(S, Len, 1, 1); do_byte(S, Len, [I]) -> do_byte(S, Len, I, I); do_byte(S, Len, [I,J]) -> do_byte(S, Len, I, J). do_byte(S, Len, I0, J0) -> %The same as for sub I1 = do_sub_m(Len, I0), J1 = do_sub_m(Len, J0), do_byte_ij(S, Len, I1, J1). do_byte_ij(S, Len, I, J) when I < 1 -> do_byte_ij(S, Len, 1, J); do_byte_ij(S, Len, I, J) when J > Len -> do_byte_ij(S, Len, I, Len); do_byte_ij(_, _, I, J) when I > J -> [nil]; do_byte_ij(S, _, I, J) -> [ N || N <- binary_to_list(S, I, J) ]. %% char(...) -> String %% Return string of the numerical arguments. char(_, [nil], St) -> {[<<>>],St}; char(_, As, St) -> case luerl_lib:args_to_integers(As) of error -> badarg_error(char, As, St); Bs -> %% Errors here also become lua_error. try String = list_to_binary(Bs), {[String],St} catch _:_ -> badarg_error(char, As, St) end end. %% dump(Function) -> String. %% Return a string with binary representation of Function. -spec dump(_, [_], _) -> no_return(). dump(_, As, St) -> badarg_error(dump, As, St). %% find(String, Pattern [, Init [, Plain]]) -> [Indice]. %% Return first occurrence of Pattern in String. find(_, As, St0) -> try do_find(As, St0) catch throw:{error,E,St1} -> lua_error(E, St1); throw:{error,E} -> lua_error(E, St0) end. do_find([A1,A2], St) -> do_find([A1,A2,1.0], St); do_find([A1,A2,A3], St) -> do_find([A1,A2,A3,nil], St); do_find(As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_string,lua_integer,lua_bool]) of [S,P,I,Pl] -> {do_find(S, byte_size(S), P, I, Pl),St}; _ -> throw({error,{badarg,find,As},St}) %nil, [_] or [] end. test_do_find(S, Pat, I, Pl) -> do_find(S, byte_size(S), Pat, I, Pl). %% do_find(String, Length, Pattern, Start, Plain) -> [Return]. %% Adjust the starting index and find the string. do_find(_, L, _, I, _) when I > L+1 -> [nil]; do_find(S, L, Pat, I, Pl) when I < -L -> do_find(S, L, Pat, 1, Pl); do_find(S, L, Pat, I, Pl) when I < 0 -> do_find(S, L, Pat, L+I+1, Pl); do_find(S, L, Pat, 0, Pl) -> do_find(S, L, Pat, 1, Pl); do_find(S, L, Pat, I, true) -> %Plain text search string case binary:match(S, Pat, [{scope,{I-1,L-I+1}}]) of {Fs,Fl} -> [Fs+1,Fs+Fl]; nomatch -> [nil] end; do_find(S, L, Pat0, I, false) -> %Pattern search string case pat(binary_to_list(Pat0)) of {ok,{Pat1,_},_} -> L1 = L - I + 1, %Length of substring S1 = binary_part(S, I-1, L1), %Start searching from I case match_loop(S1, L1, Pat1, 1) of [{_,P,Len}|Cas] -> %Matches P1 = P + I - 1, %Position in original string [P1,P1+Len-1|match_caps(Cas, S, I)]; [] -> [nil] %No match end; {error,E} -> throw({error,E}) end. %% format([Format|Args], State) -> {[String],State}. %% Format a string. All errors are badarg errors. %% Do all the work in luerl_string_format but generate errors here. format(_, [F|As], St0) -> try %% io:format("format ~w\n", [element(1,luerl_lib_string_format:format(F, As, St0))]), luerl_lib_string_format:format(F, As, St0) catch %% If we have no specific error, default is badarg. throw:{error,E,St1} -> lua_error(E, St1); throw:{error,E} -> lua_error(E, St0); _:_ -> badarg_error(format, [F|As], St0) %% ?CATCH(C, E, Stack) %% error({C,E,Stack}) end; format(_, As, St) -> badarg_error(format, As, St). -spec gmatch(_, [_], _) -> no_return(). %To keep dialyzer quiet %% gmatch(String, Pattern) -> [Function]. gmatch(_, As, St) -> badarg_error(gmatch, As, St). %% gsub(String, Pattern, Repl [, N]) -> [String] gsub(_, As, St0) -> try do_gsub(As, St0) catch throw:{error,E,St1} -> lua_error(E, St1); throw:{error,E} -> lua_error(E, St0) end. do_gsub(As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_string,lua_any,lua_integer]) of [S,P,R,N] when N > 0 -> do_gsub(S, byte_size(S), P, R, N, St); [S,P,R] -> %'all' bigger than any number do_gsub(S, byte_size(S), P, R, all, St); _ -> throw({error,{badarg,gsub,As},St}) end. test_gsub(S, P, N) -> {ok,{Pat,_},_} = pat(binary_to_list(P)), gsub_match_loop(S, byte_size(S), Pat, 1, 1, N). do_gsub(S, L, Pat0, R, N, St0) -> case pat(binary_to_list(Pat0)) of {ok,{Pat1,_},_} -> Fs = gsub_match_loop(S, L, Pat1, 1, 1, N), {Ps,St1} = gsub_repl_loop(Fs, S, 1, L, R, St0), {[iolist_to_binary(Ps),length(Fs)],St1}; {error,E} -> throw({error,E}) end. %% gsub_match_loop(S, L, Pat, I, C, N) -> [Cas]. %% Return the list of Cas's for each match. gsub_match_loop(_, _, _, _, C, N) when C > N -> []; gsub_match_loop(<<>>, _, Pat, I, _, _) -> %It can still match at end! case match_pat(<<>>, Pat, I) of {match,Cas,_,_} -> [Cas]; nomatch -> [] end; gsub_match_loop(S0, L, Pat, I0, C, N) -> case match_pat(S0, Pat, I0) of {match,Cas,_,I0} -> %Zero length match S1 = binary_part(S0, 1, L-I0), [Cas|gsub_match_loop(S1, L, Pat, I0+1, C+1, N)]; {match,Cas,S1,I1} -> [Cas|gsub_match_loop(S1, L, Pat, I1, C+1, N)]; nomatch -> S1 = binary_part(S0, 1, L-I0), gsub_match_loop(S1, L, Pat, I0+1, C, N) end. %% gsub_repl_loop([Cas], String, Index, Length, Reply, State) -> %% {iolist,State}. %% Build the return string as an iolist processing each match and %% filling in with the original string. gsub_repl_loop([[{_,F,Len}|_]=Cas|Fs], S, I, L, R, St0) -> %% io:fwrite("grl: ~p\n", [{Cas,S,R}]), {Rep,St1} = gsub_repl(Cas, S, R, St0), %% io:fwrite("grl->~p\n", [{Rep}]), {Ps,St2} = gsub_repl_loop(Fs, S, F+Len, L, R, St1), {[binary_part(S, I-1, F-I),Rep|Ps],St2}; gsub_repl_loop([], S, I, L, _, St) -> {[binary_part(S, I-1, L-I+1)],St}. gsub_repl(Cas, S, #tref{}=T, St0) -> case Cas of %Export both Ca and Key [Ca] -> Key = match_cap(Ca, S); [Ca,Ca1|_] -> Key = match_cap(Ca1, S) end, {R,St1} = luerl_emul:get_table_key(T, Key, St0), {[gsub_repl_val(S, R, Ca)],St1}; gsub_repl(Cas0, S, Repl, St0) when ?IS_FUNCTION(Repl) -> case Cas0 of %Export both Ca and Args [Ca] -> Args = [match_cap(Ca, S)]; [Ca|Cas] -> Args = match_caps(Cas, S) end, {Rs,St1} = luerl_emul:functioncall(Repl, Args, St0), {[gsub_repl_val(S, luerl_lib:first_value(Rs), Ca)],St1}; gsub_repl(Cas, S, Repl, St) -> %Replace string case luerl_lib:arg_to_list(Repl) of error -> {[],St}; R -> {gsub_repl_str(Cas, S, R),St} end. gsub_repl_str(Cas, S, [$%,$%|R]) -> [$%|gsub_repl_str(Cas, S, R)]; gsub_repl_str(Cas, S, [$%,$0|R]) -> Cstr = luerl_lib:arg_to_string(match_cap(hd(Cas), S)), %Force to string! [Cstr|gsub_repl_str(Cas, S, R)]; gsub_repl_str(Cas, S, [$%,C|R]) when C >= $1, C =< $9 -> case lists:keysearch(C-$0, 1, Cas) of {value,Ca} -> Cstr = luerl_lib:arg_to_string(match_cap(Ca, S)), %Force to string! [Cstr|gsub_repl_str(Cas, S, R)]; false -> throw({error,{illegal_index,capture,C-$0}}) end; gsub_repl_str(Cas, S, [C|R]) -> [C|gsub_repl_str(Cas, S, R)]; gsub_repl_str(_, _, []) -> []. %% Return string or original match. gsub_repl_val(S, Val, Ca) -> case luerl_lib:arg_to_string(Val) of error -> match_cap(Ca, S); %Use original match Str -> Str end. %% len(String) -> Length. len(_, [A|_], St) when is_binary(A) -> {[byte_size(A)],St}; len(_, [A|_], St) when is_number(A) -> {[length(luerl_lib:number_to_list(A))],St}; len(_, As, St) -> badarg_error(len, As, St). %% lower(String) -> String. lower(_, As, St) -> case luerl_lib:conv_list(As, [erl_list]) of [S] -> {[list_to_binary(string:to_lower(S))],St}; _ -> badarg_error(lower, As, St) %nil or [] end. %% match(String, Pattern [, Init]) -> [Match]. match(_, As, St0) -> try do_match(As, St0) catch throw:{error,E,St1} -> lua_error(E, St1); throw:{error,E} -> lua_error(E, St0) end. do_match([A1,A2], St) -> do_match([A1,A2,1.0], St); do_match(As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_string,lua_integer]) of [S,P,I] -> {do_match(S, byte_size(S), P, I),St}; _ -> throw({error,{badarg,match,As},St}) end. %% do_match(String, Length, Pattern, Start) -> [Return]. %% Adjust the starting index and find the match. do_match(_, L, _, I) when I > L -> [nil]; %Shuffle values do_match(S, L, Pat, I) when I < -L -> do_match(S, L, Pat, 1); do_match(S, L, Pat, I) when I < 0 -> do_match(S, L, Pat, L+I+1); do_match(S, L, Pat, 0) -> do_match(S, L, Pat, 1); do_match(S, L, Pat0, I) -> case pat(binary_to_list(Pat0)) of %"Compile" the pattern {ok,{Pat1,_},_} -> L1 = L - I + 1, %Length of substring S1 = binary_part(S, I-1, L1), %Start searching from I case match_loop(S1, L1, Pat1, 1) of [{_,P,Len}] -> %Only top level match P1 = P + I - 1, %Position in original string [binary_part(S, P1-1, Len)]; [_|Cas] -> %Have sub matches match_caps(Cas, S1); [] -> [nil] %No match end; {error,E} -> throw({error,E}) end. %% match_loop(String, Length, Pattern, Index) -> Cas | []. %% Step down the string trying to find a match. match_loop(S, L, Pat, I) when I > L -> %It can still match at end! case match_pat(S, Pat, I) of {match,Cas,_,_} -> Cas; nomatch -> [] %Now we haven't found it end; match_loop(S0, L, Pat, I) -> case match_pat(S0, Pat, I) of {match,Cas,_,_} -> Cas; nomatch -> S1 = binary_part(S0, 1, L-I), match_loop(S1, L, Pat, I+1) end. %% match_cap(Capture, String [, Init]) -> Capture. %% match_caps(Captures, String [, Init]) -> Captures. %% Get the captures. The string is the whole string not just from %% Init. match_cap(Ca, S) -> match_cap(Ca, S, 1). match_cap({_,P,Len}, _, I) when Len < 0 -> %Capture position P+I-1; match_cap({_,P,Len}, S, I) -> %Capture binary_part(S, P+I-2, Len). %Binaries count from 0 match_caps(Cas, S) -> match_caps(Cas, S, 1). match_caps(Cas, S, I) -> [ match_cap(Ca, S, I) || Ca <- Cas ]. %% rep(String, N [, Separator]) -> [String]. rep(_, [A1,A2], St) -> rep(nil, [A1,A2,<<>>], St); rep(_, [_,_,_|_]=As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_integer,lua_string]) of [S,I,Sep] -> Part = [Sep,S], if I > 100 -> %% For many repetitions. I1 = (I-1) div 100, I2 = (I-1) rem 100, D100 = iolist_to_binary(lists:duplicate(100, Part)), {[iolist_to_binary([S, lists:duplicate(I1, D100), lists:duplicate(I2, Part)])], St}; I > 0 -> {[iolist_to_binary([S|lists:duplicate(I-1, Part)])],St}; true -> {[<<>>],St} end; error -> %Error or bad values badarg_error(rep, As, St) end; rep(_, As, St) -> badarg_error(rep, As, St). %% reverse([String], State) -> {[Res],St}. reverse(_, [A|_], St) when is_binary(A) ; is_number(A) -> S = luerl_lib:arg_to_list(A), {[list_to_binary(lists:reverse(S))],St}; reverse(_, As, St) -> badarg_error(reverse, As, St). %% sub([String, I [, J]], State) -> {[Res],State}. sub(_, As, St) -> case luerl_lib:conv_list(As, [lua_string,lua_integer,lua_integer]) of [S,I|Js] -> Len = byte_size(S), Sub = do_sub(S, Len, I, Js), %Just I, or both I and J {[Sub],St}; _ -> badarg_error(sub, As, St) %nil, [_] or [] end. test_sub(S, I) -> do_sub(S, byte_size(S), I, []). test_sub(S, I, J) -> do_sub(S, byte_size(S), I, [J]). do_sub(S, _, 0, []) -> S; %Special case this do_sub(S, Len, I, []) -> do_sub_1(S, Len, I, Len); do_sub(S, Len, I, [J]) -> do_sub_1(S, Len, I, J). do_sub_1(S, Len, I0, J0) -> I1 = do_sub_m(Len, I0), J1 = do_sub_m(Len, J0), do_sub_ij(S, Len, I1, J1). do_sub_m(Len, I) when I < 0 -> Len+I+1; %Negative count from end do_sub_m(_, I) -> I. do_sub_ij(S, Len, I, J) when I < 1 -> do_sub_ij(S, Len, 1, J); do_sub_ij(S, Len, I, J) when J > Len -> do_sub_ij(S, Len, I, Len); do_sub_ij(_, _, I, J) when I > J -> <<>>; do_sub_ij(S, _, I, J) -> binary:part(S, I-1, J-I+1). %Zero-based, yuch! upper(_, [A|_], St) when is_binary(A) ; is_number(A) -> S = luerl_lib:arg_to_list(A), {[list_to_binary(string:to_upper(S))],St}; upper(_, As, St) -> badarg_error(upper, As, St). %% This is the pattern grammar used. It may actually be overkill to %% first parse the pattern as the pattern is relativey simple and we %% should be able to do it in one pass. %% %% pat -> seq : '$1'. %% seq -> single seq : ['$1'|'$2']. %% seq -> single : '$1'. %% single -> "(" seq ")" . %% single -> "[" class "]" : {char_class,char_class('$2')} %% single -> "[" "^" class "]" : {comp_class,char_class('$3')} %% single -> char "*" . %% single -> char "+" . %% single -> char "-" . %% single -> char "?" . %% single -> char . %% char -> "%" class . %% char -> "." . %% char -> char . %% The actual parser is a recursive descent implementation of the %% grammar. We leave ^ $ as normal characters and handle them %% specially in matching. pat(Cs0) -> case catch seq(Cs0, 0, 1, []) of {error,E} -> {error,E}; {P,0,Sn} -> {ok,{P,0},Sn}; {_,_,_} -> {error,invalid_capture} end. test_pat(P) -> pat(P). seq([$^|Cs], Sd, Sn, P) -> single(Cs, Sd, Sn, ['^'|P]); seq([_|_]=Cs, Sd, Sn, P) -> single(Cs, Sd, Sn, P); seq([], Sd, Sn, P) -> {lists:reverse(P),Sd,Sn}. single([$(|Cs], Sd, Sn, P) -> single(Cs, Sd+1, Sn+1, [{'(',Sn}|P]); single([$)|_], 0, _, _) -> throw({error,invalid_capture}); single([$)|Cs], Sd, Sn, P) -> single(Cs, Sd-1, Sn, [')'|P]); single([$[|Cs], Sd, Sn, P) -> char_set(Cs, Sd, Sn, P); single([$.|Cs], Sd, Sn, P) -> singlep(Cs, Sd, Sn, ['.'|P]); single([$%|Cs], Sd, Sn, P) -> char_class(Cs, Sd, Sn, P); single([$$], Sd, Sn, P) -> {lists:reverse(P, ['\$']),Sd,Sn}; single([C|Cs], Sd, Sn, P) -> singlep(Cs, Sd, Sn, [C|P]); single([], Sd, Sn, P) -> {lists:reverse(P),Sd,Sn}. singlep([$*|Cs], Sd, Sn, [Char|P]) -> single(Cs, Sd, Sn, [{kclosure,Char}|P]); singlep([$+|Cs], Sd, Sn, [Char|P]) -> single(Cs, Sd, Sn, [{pclosure,Char}|P]); singlep([$-|Cs], Sd, Sn, [Char|P]) -> single(Cs, Sd, Sn, [{mclosure,Char}|P]); singlep([$?|Cs], Sd, Sn, [Char|P]) -> single(Cs, Sd, Sn, [{optional,Char}|P]); singlep(Cs, Sd, Sn, P) -> single(Cs, Sd, Sn, P). char_set([$^|Cs], Sd, Sn, P) -> char_set(Cs, Sd, Sn, P, comp_set); char_set(Cs, Sd, Sn, P) -> char_set(Cs, Sd, Sn, P, char_set). char_set(Cs0, Sd, Sn, P, Tag) -> case char_set(Cs0) of {Set,[$]|Cs1]} -> singlep(Cs1, Sd, Sn, [{Tag,Set}|P]); {_,_} -> throw({error,invalid_char_set}) end. char_set([$]|Cs]) -> char_set(Cs, [$]]); %Must special case this char_set(Cs) -> char_set(Cs, []). char_set([$]|_]=Cs, Set) -> {Set,Cs}; %We are at the end char_set([$%,C|Cs], Set) -> char_set(Cs, [char_class(C)|Set]); char_set([C1,$-,C2|Cs], Set) when C2 =/= $] -> char_set(Cs, [{C1,C2}|Set]); char_set([C|Cs], Set) -> char_set(Cs, [C|Set]); char_set([], Set) -> {Set,[]}. %We are at the end %% char_class([$f,$[|Cs], Sd, Sn, P) -> %% char_set(Cs, Sd, Sn, [frontier|P]); char_class([$f|_], _, _, _) -> throw({error,invalid_pattern}); char_class([$b,L,R|Cs], Sd, Sn, P) -> singlep(Cs, Sd, Sn, [{balance,L,R}|P]); char_class([C|Cs], Sd, Sn, P) -> singlep(Cs, Sd, Sn, [char_class(C)|P]); char_class([], _, _, _) -> throw({error,invalid_pattern}). char_class($a) -> 'a'; char_class($A) -> 'A'; char_class($c) -> 'c'; char_class($C) -> 'C'; char_class($d) -> 'd'; char_class($D) -> 'D'; char_class($g) -> 'g'; char_class($G) -> 'G'; char_class($l) -> 'l'; char_class($L) -> 'L'; char_class($p) -> 'p'; char_class($P) -> 'P'; char_class($s) -> 's'; char_class($S) -> 'S'; char_class($u) -> 'u'; char_class($U) -> 'U'; char_class($w) -> 'w'; char_class($W) -> 'W'; char_class($x) -> 'x'; char_class($X) -> 'X'; char_class($z) -> 'z'; %Deprecated char_class($Z) -> 'Z'; char_class(C) -> %Only non-alphanum allowed case is_w_char(C) of true -> throw({error,{invalid_char_class,C}}); false -> C end. test_match_pat(S, P, I) -> {ok,{Pat,_},_} = pat(P), io:fwrite("tdm: ~p\n", [{Pat}]), match_pat(S, Pat, I). %% match_pat(String, Pattern, Index) -> {match,[Capture],Rest,Index} | nomatch. %% Try and match the pattern with the string *at the current %% position*. No searching. match_pat(S0, P0, I0) -> case match_pat(P0, S0, I0, [{0,I0}], []) of {match,S1,I1,_,Cas} ->{match,Cas,S1,I1}; {nomatch,_,_,_,_,_} -> nomatch end. match_pat(['\$']=Ps, Cs, I, Ca, Cas) -> %Match only end of string case Cs of <<>> -> match_pat([], <<>>, I, Ca, Cas); _ -> {nomatch,Ps,Cs,I,Ca,Cas} end; match_pat(['^'|Ps]=Ps0, Cs, I, Ca, Cas) -> %Match beginning of string if I =:= 1 -> match_pat(Ps, Cs, 1, Ca, Cas); true -> {nomatch,Ps0,Cs,I,Cs,Cas} end; match_pat([{'(',Sn},')'|P], Cs, I, Ca, Cas) -> match_pat(P, Cs, I, Ca, save_cap(Sn, I, -1, Cas)); match_pat([{'(',Sn}|P], Cs, I, Ca, Cas) -> match_pat(P, Cs, I, [{Sn,I}|Ca], Cas); match_pat([')'|P], Cs, I, [{Sn,S}|Ca], Cas) -> match_pat(P, Cs, I, Ca, save_cap(Sn, S, I-S, Cas)); match_pat([{kclosure,P}=K|Ps], Cs, I, Ca, Cas) -> %%io:fwrite("dm: ~p\n", [{[P,K|Ps],Cs,I,Ca,Cas}]), case match_pat([P,K|Ps], Cs, I, Ca, Cas) of %First try with it {match,_,_,_,_}=M -> M; {nomatch,_,_,_,_,_} -> %Else try without it match_pat(Ps, Cs, I, Ca, Cas) end; match_pat([{pclosure,P}|Ps], Cs, I, Ca, Cas) -> %The easy way match_pat([P,{kclosure,P}|Ps], Cs, I, Ca, Cas); match_pat([{mclosure,P}=K|Ps], Cs, I, Ca, Cas) -> case match_pat(Ps, Cs, I, Ca, Cas) of %First try without it {match,_,_,_,_}=M -> M; {nomatch,_,_,_,_,_} -> %Else try with it match_pat([P,K|Ps], Cs, I, Ca, Cas) end; match_pat([{optional,P}|Ps], Cs, I, Ca, Cas) -> case match_pat([P|Ps], Cs, I, Ca, Cas) of %First try with it {match,_,_,_,_}=M -> M; {nomatch,_,_,_,_,_} -> %Else try without it match_pat(Ps, Cs, I, Ca, Cas) end; match_pat([{char_set,Set}|Ps]=Ps0, <>=Cs0, I, Ca, Cas) -> case match_char_set(Set, C) of true -> match_pat(Ps, Cs, I+1, Ca, Cas); false -> {nomatch,Ps0,Cs0,I,Ca,Cas} end; match_pat([{comp_set,Set}|Ps]=Ps0, <>=Cs0, I, Ca, Cas) -> case match_char_set(Set, C) of true -> {nomatch,Ps0,Cs0,I,Ca,Cas}; false -> match_pat(Ps, Cs, I+1, Ca, Cas) end; match_pat([{balance,L,R}|Ps]=Ps0, <>=Cs0, I0, Ca, Cas) -> case balance(Cs1, I0+1, L, R, 1) of {ok,Cs2,I1} -> match_pat(Ps, Cs2, I1, Ca, Cas); error -> {nomatch,Ps0,Cs0,I0,Ca,Cas} end; match_pat(['.'|Ps], <<_,Cs/binary>>, I, Ca, Cas) -> %Matches anything match_pat(Ps, Cs, I+1, Ca, Cas); match_pat([A|Ps]=Ps0, <>=Cs0, I, Ca, Cas) when is_atom(A) -> case match_class(A, C) of true -> match_pat(Ps, Cs, I+1, Ca, Cas); false -> {nomatch,Ps0,Cs0,I,Ca,Cas} end; match_pat([C|Ps], <>, I, Ca, Cas) -> match_pat(Ps, Cs, I+1, Ca, Cas); match_pat([], Cs, I, [{Sn,S}|Ca], Cas) -> {match,Cs,I,Ca,[{Sn,S,I-S}|Cas]}; match_pat(Ps, Cs, I, Ca, Cas) -> {nomatch,Ps,Cs,I,Ca,Cas}. %% save_cap(N, Position, Length, Captures) -> Captures. %% Add a new capture to the list in the right place, ordered. save_cap(N, P, L, [{N1,_,_}=Ca|Cas]) when N > N1 -> [Ca|save_cap(N, P, L, Cas)]; save_cap(N, P, L, Cas) -> [{N,P,L}|Cas]. %% MUST first check for right char, this in case of L == R! balance(<>, I, L, R, D) -> if D =:= 1 -> {ok,Cs,I+1}; true -> balance(Cs, I+1, L, R, D-1) end; balance(<>, I, L, R, D) -> balance(Cs, I+1, L, R, D+1); balance(<<_,Cs/binary>>, I, L, R, D) -> balance(Cs, I+1, L, R, D); balance(<<>>, _, _, _, _) -> error. match_class('a', C) -> is_a_char(C); match_class('A', C) -> not is_a_char(C); match_class('c', C) -> is_c_char(C); match_class('C', C) -> not is_c_char(C); match_class('d', C) -> is_d_char(C); match_class('D', C) -> not is_d_char(C); match_class('g', C) -> is_g_char(C); match_class('G', C) -> not is_g_char(C); match_class('l', C) -> is_l_char(C); match_class('L', C) -> not is_l_char(C); match_class('p', C) -> is_p_char(C); match_class('P', C) -> not is_p_char(C); match_class('s', C) -> is_s_char(C); match_class('S', C) -> not is_s_char(C); match_class('u', C) -> is_u_char(C); match_class('U', C) -> not is_u_char(C); match_class('w', C) -> is_w_char(C); match_class('W', C) -> not is_w_char(C); match_class('x', C) -> is_x_char(C); match_class('X', C) -> not is_x_char(C); match_class('z', C) -> is_z_char(C); %Deprecated match_class('Z', C) -> not is_z_char(C). match_char_set([{C1,C2}|_], C) when C >= C1, C=< C2 -> true; match_char_set([A|Set], C) when is_atom(A) -> match_class(A, C) orelse match_char_set(Set, C); match_char_set([C|_], C) -> true; match_char_set([_|Set], C) -> match_char_set(Set, C); match_char_set([], _) -> false. %% Test for various character types. is_a_char(C) -> %All letters is_l_char(C) orelse is_u_char(C). is_c_char(C) when C >= 0, C =< 31 -> true; %All control characters is_c_char(C) when C >= 128, C =< 159 -> true; is_c_char(_) -> false. is_d_char(C) -> (C >= $0) and (C =< $9). %All digits is_g_char(C) when C >= 33, C =< 126 -> true; %All printable characters is_g_char(C) when C >= 161, C =< 255 -> true; is_g_char(_) -> false. is_l_char(C) when C >= $a, C =< $z -> true; %All lowercase letters is_l_char(C) when C >= 224, C =< 246 -> true; is_l_char(C) when C >= 248, C =< 255 -> true; is_l_char(_) -> false. is_p_char(C) when C >= 33, C =< 47 -> true; %All punctutation characters is_p_char(C) when C >= 58, C =< 63 -> true; is_p_char(C) when C >= 91, C =< 96 -> true; is_p_char(126) -> true; is_p_char(C) when C >= 161, C =< 191 -> true; is_p_char(215) -> true; is_p_char(247) -> true; is_p_char(_) -> false. is_s_char(C) when C >= 9, C =< 13 -> true; %Space characters is_s_char(32) -> true; is_s_char(160) -> true; is_s_char(_) -> false. is_u_char(C) when C >= $A, C =< $Z -> true; %All uppercase letters is_u_char(C) when C >= 192, C =< 214 -> true; is_u_char(C) when C >= 216, C =< 223 -> true; is_u_char(_) -> false. is_w_char(C) -> %All alphanumeric characters is_a_char(C) orelse is_d_char(C). is_x_char(C) when C >= $a, C =< $f -> true; %All hexadecimal characters is_x_char(C) when C >= $A, C =< $F -> true; is_x_char(C) -> is_d_char(C). is_z_char(C) -> C =:= 0. %The zero character, deprecated %% match_class('a', C) -> (char_table(C) band ?_A) =/= 0; %% match_class('A', C) -> (char_table(C) band ?_A) =:= 0. %% char_table(C) when C >= 0, C =< 31 -> ?_C; %% char_table(C) when C >= 65, C =< 91 -> ?_U bor ?_A; %% char_table(C) when C >= 97, C =< 123 -> ?_L; rvirding-luerl-772b25e/src/luerl_lib_os_date.erl0000664000232200023220000001740515114130670022335 0ustar debalancedebalance%% Copyright (c) 2023-2024 Robert Virding %% %% 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. %% File : lib_date_support.erl %% Author : Mark Meeus %% Purpose : Date Support Module -module(luerl_lib_os_date). -include("luerl.hrl"). ?MODULEDOC(false). -export([format/2]). %% IMPLEMENTED FORMATS: %% %A - Full weekday name (eg. Wednesday) %% %a - Abbreviated weekday name (eg. Wed) %% %B - Full month name (eg. September) %% %b - Abbreviated month name (eg. Sep) %% %c - date and time (e.g. Wed Jan 24 12:27:59 2024) %% %D - date (e.g. 09/16/98) %% %d - Day of month as decimal number (01 - 31) %% %e - Day of month as decimal number ( 1 - 31) %% %F - date (e.g. 2024-01-31) %% %H - Hour in 24-hour format (00 - 23) %% %I - Hour in 12-hour format (01 - 12) %% %M - Minute as decimal number (00 - 59) %% %m - Month as decimal number (01 - 12) %% %n - Newline character %% %P - As %p but lowercase %% %p - Current locale’s A.M./P.M. indicator for 12-hour clock (eg. AM/PM) %% %S - Second as decimal number (00 - 59) %% %T - time (e.g. 23:48:10) %% %t - Tab character %% %u - Weekday as decimal number (1 - 7; Monday is 1) %% %V - IOS week number (01 - 53) %% %W - Week of year as decimal number, with Monday as first day of week 1 (00 - 53) %% %w - Weekday as decimal number (0 - 6; Sunday is 0) %% %X - time (e.g., 23:48:10) %% %x - date (e.g., 09/16/98) %% %Y - Year with century, as decimal number (eg. 2007) %% %y - Year without century, as decimal number (00 - 99) (eg. 07) %% %Z - Time-zone name or abbreviation; no characters if time zone is unknown %% %% - Percent sign %% NOT IMPLEMENTED: %% %j - Day of year as decimal number (001 - 366) %% %U - Week of year as decimal number, with Sunday as first day of week 1 (00 - 53) %% format(DateTime, Format) -> Formatted. %% *t format returns a table with the datetime values. format(DateTime, <<"*t">>) -> {{Ye,Mo,Da}=Date,{Ho,Mi,Sec}} = DateTime, [ {<<"year">>, Ye}, {<<"month">>, Mo}, {<<"day">>, Da}, {<<"hour">>, Ho}, {<<"min">>, Mi}, {<<"sec">>, Sec}, {<<"wday">>, get_day_number(Date)+1} ]; format(DateTime, Format) -> Patterns = [ <<"%A">>, <<"%a">>, <<"%B">>, <<"%b">>, <<"%c">>, <<"%D">>, <<"%d">>, <<"%e">>, <<"%F">>, <<"%H">>, <<"%I">>, <<"%M">>, <<"%m">>, <<"%n">>, <<"%P">>, <<"%p">>, <<"%S">>, <<"%T">>, <<"%t">>, <<"%u">>, <<"%V">>, <<"%W">>, <<"%w">>, <<"%X">>, <<"%x">>, <<"%Y">>, <<"%y">>, <<"%Z">>, <<"%%">> ], lists:foldl(fun(Pat, Str) -> replace_pattern(Str, DateTime, Pat) end, Format, Patterns). replace_pattern(Str, DateTime, Pat) -> case re:run(Str, Pat) of nomatch -> Str; {match, _} -> {Format, Val} = get_pattern_values(Pat, DateTime), Formatted = io_lib:fwrite(Format, Val), re:replace(Str, Pat, Formatted, [{return, binary}, global]) end. %% get_pattern_values(Patters, DateTime) -> {FormatString, Values}. get_pattern_values(<<"%A">>, {Date, _}) -> {"~s", [get_day_name(get_day_number(Date))]}; get_pattern_values(<<"%a">>, {Date, _}) -> {"~s", [get_abbreviated_day_name(get_day_number(Date))]}; get_pattern_values(<<"%B">>, {{_, Mo, _}, _}) -> {"~s", [get_month_name(Mo)]}; get_pattern_values(<<"%b">>, {{_, Mo, _}, _}) -> {"~s", [get_abbreviated_month_name(Mo)]}; get_pattern_values(<<"%c">>, {{Ye, Mo, Da}=Date, {Ho, Mi, Sec}}) -> {"~s ~s ~.2w ~.2.0w:~.2.0w:~.2.0w ~.4.0w", [get_abbreviated_day_name(get_day_number(Date)), get_abbreviated_month_name(Mo),Da, Ho,Mi,Sec, Ye]}; get_pattern_values(<<"%D">>, {{Ye, Mo, Da}, _}) -> {"~.2.0w/~.2.0w/~.2.0w", [Mo, Da, Ye rem 100]}; get_pattern_values(<<"%d">>, {{_, _, Da}, _}) -> {"~.2.0w", [Da]}; get_pattern_values(<<"%e">>, {{_, _, Da}, _}) -> {"~.2w", [Da]}; get_pattern_values(<<"%F">>, {{Ye, Mo, Da}, _}) -> {"~.4.0w-~.2.0w-~.2.0w", [Ye, Mo, Da]}; get_pattern_values(<<"%H">>, {_, {Ho, _, _}}) -> {"~.2.0w", [Ho]}; get_pattern_values(<<"%I">>, {_, {Ho, _, _}}) -> {"~.2.0w", [get_am_pm_hour(Ho)]}; get_pattern_values(<<"%M">>, {_, {_, Mi, _}}) -> {"~.2.0w", [Mi]}; get_pattern_values(<<"%m">>, {{_, Mo, _}, _}) -> {"~.2.0w", [Mo]}; get_pattern_values(<<"%n">>, _) -> {"\n", []}; get_pattern_values(<<"%P">>, {_, {Ho, _, _}}) -> Val = if Ho < 12 -> <<"am">>; true -> <<"pm">> end, {"~s", [Val]}; get_pattern_values(<<"%p">>, {_, {Ho, _, _}}) -> Val = if Ho < 12 -> <<"AM">>; true -> <<"PM">> end, {"~s", [Val]}; get_pattern_values(<<"%S">>, {_, {_, _, Sec}}) -> {"~.2.0w", [Sec]}; get_pattern_values(<<"%T">>, {_, {Ho, Mi, Sec}}) -> {"~.2.0w:~.2.0w:~.2.0w", [Ho, Mi, Sec]}; get_pattern_values(<<"%t">>, _) -> {"\t", []}; get_pattern_values(<<"%u">>, {Date, _}) -> {"~.1.0w", [calendar:day_of_the_week(Date)]}; % Mon = 1 Sun = 7 get_pattern_values(<<"%V">>, {Date, _}) -> {_Year,Week} = calendar:iso_week_number(Date), {"~.2.0w", [Week]}; get_pattern_values(<<"%W">>, {Date, _}) -> {_Year,Week} = calendar:iso_week_number(Date), {"~.2.0w", [Week]}; get_pattern_values(<<"%w">>, {Date, _}) -> {"~.1.0w", [get_day_number(Date)]}; % Sun = 0 Sat = 6 get_pattern_values(<<"%X">>, {_, {Ho, Mi, Sec}}) -> {"~.2.0w:~.2.0w:~.2.0w", [Ho, Mi, Sec]}; get_pattern_values(<<"%x">>, {{Ye, Mo, Da}, _}) -> {"~.2.0w/~.2.0w/~.2.0w", [Mo, Da, Ye rem 100]}; get_pattern_values(<<"%Y">>, {{Ye, _, _}, _}) -> {"~.4.0w", [Ye]}; get_pattern_values(<<"%y">>, {{Ye, _, _}, _}) -> {"~.2.0w", [Ye rem 100]}; get_pattern_values(<<"%Z">>, _) -> {"", []}; get_pattern_values(<<"%%">>, _) -> {"~c", [$%]}. %% get_day_number(Date) -> DayNumber. %% This is US so Sunday is day 1. get_day_number(Date) -> calendar:day_of_the_week(Date) rem 7. get_am_pm_hour(0) -> 12; get_am_pm_hour(H) when H > 12 -> H - 12; get_am_pm_hour(H) -> H. get_month_name(1) -> "January"; get_month_name(2) -> "February"; get_month_name(3) -> "March"; get_month_name(4) -> "April"; get_month_name(5) -> "May"; get_month_name(6) -> "June"; get_month_name(7) -> "July"; get_month_name(8) -> "August"; get_month_name(9) -> "September"; get_month_name(10) -> "October"; get_month_name(11) -> "November"; get_month_name(12) -> "December". get_abbreviated_month_name(1) -> "Jan"; get_abbreviated_month_name(2) -> "Feb"; get_abbreviated_month_name(3) -> "Mar"; get_abbreviated_month_name(4) -> "Apr"; get_abbreviated_month_name(5) -> "May"; get_abbreviated_month_name(6) -> "Jun"; get_abbreviated_month_name(7) -> "Jul"; get_abbreviated_month_name(8) -> "Aug"; get_abbreviated_month_name(9) -> "Sep"; get_abbreviated_month_name(10) -> "Oct"; get_abbreviated_month_name(11) -> "Nov"; get_abbreviated_month_name(12) -> "Dec". get_day_name(0) -> "Sunday"; get_day_name(1) -> "Monday"; get_day_name(2) -> "Tuesday"; get_day_name(3) -> "Wednesday"; get_day_name(4) -> "Thursday"; get_day_name(5) -> "Friday"; get_day_name(6) -> "Saturday". get_abbreviated_day_name(0) -> "Sun"; get_abbreviated_day_name(1) -> "Mon"; get_abbreviated_day_name(2) -> "Tue"; get_abbreviated_day_name(3) -> "Wed"; get_abbreviated_day_name(4) -> "Thu"; get_abbreviated_day_name(5) -> "Fri"; get_abbreviated_day_name(6) -> "Sat". rvirding-luerl-772b25e/src/luerl_lib.erl0000664000232200023220000003416415114130670020640 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% %% 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. %% File : luerl_lib.erl %% Author : Robert Virding %% Purpose : Luerl libraries. %% A collection of useful functions. Those with '_' in their names %% generate Erlang data types while those with generate Lua data types %% (floats and binaries). -module(luerl_lib). -include("luerl.hrl"). ?MODULEDOC(false). -export([lua_error/2,badarg_error/3,badarith_error/3, format_error/1,format_value/1]). -export([boolean_value/1,first_value/1]). -export([number_to_list/1]). -export([arg_to_list/1,args_to_lists/1,args_to_lists/2]). -export([arg_to_number/1,arg_to_number/2,args_to_numbers/1,args_to_numbers/2]). -export([arg_to_integer/1,args_to_integers/1,args_to_integers/2]). -export([arg_to_float/1,args_to_floats/1,args_to_floats/2]). -export([arg_to_string/1,args_to_strings/1,args_to_strings/2]). -export([conv_list/2,conv_list/3]). -export([tostring/2]). -dialyzer({[no_return], [lua_error/2, badarg_error/3, badarith_error/3]}). -spec lua_error(_,_) -> no_return(). -spec badarg_error(_,_,_) -> no_return(). %% lua_error(Error, State) -> no_return(). %% badarg_error(What, Args, State) -> no_return(). %% badarith_error(What, Args, State) -> no_return(). lua_error(E, St) -> error({lua_error,E,St}). badarg_error(What, Args, St) -> lua_error({badarg,What,Args}, St). badarith_error(What, Args, St) -> lua_error({badarith,What,Args}, St). %% format_error(LuaError) -> ErrorString. %% The ErrorString is a UTF-8 encoded binary. The UTF-8 encoded %% codepoints can come from Lua/Luerl strings. Some of these use %% same text as Lua error string, so be careful if modifying them. format_error({badarg,Where,Args}) -> %% Note Args is a list which must be kept as its own list! format_error("bad argument ~ts to ~ts", [Args,Where]); format_error({badarith,Op,Args}) -> %% Note Args is a list which must be kept as its own list! format_error("bad arithmetic ~ts on ~ts", [Op,Args]); format_error({illegal_index,Where,Index}) -> format_error("invalid index in ~ts: ~ts", [Where,Index]); format_error({illegal_value,Where,Val}) -> format_error("invalid value in ~ts: ~ts", [Where,Val]); format_error({illegal_value,Val}) -> format_error("invalid value: ~ts", [Val]); format_error({illegal_comp,Where}) -> format_error(<<"illegal comparison in ~ts">>, [Where]); %% format_error({invalid_order,Where}) -> %Keep text! %% format_error(<<"invalid order function in ~w">>, [Where]); format_error({undefined_function,Name}) -> format_error(<<"undefined function ~ts">>, [Name]); format_error({undefined_method,Object,Name}) -> format_error(<<"undefined method ~ts in ~ts">>, [Name,Object]); format_error(illegal_return_value) -> <<"illegal format of return value">>; format_error({illegal_return_value,Func}) -> format_error(<<"illegal format of return value to ~ts">>, [Func]); %% Pattern errors. format_error(invalid_pattern) -> %Keep text! <<"malformed pattern">>; format_error(invalid_capture) -> %Keep text! <<"malformed pattern">>; format_error({invalid_char_class,C}) -> %Keep text! Msg = io_lib:format("malformed pattern (class ~c)", [C]), unicode:characters_to_binary(Msg); format_error(invalid_char_set) -> %Keep text! <<"malformed pattern (missing ']')">>; %% Illegal or undefined ops. format_error({illegal_op,Op}) -> format_error("illegal op: ~ts", [Op]); format_error({no_module,Mod}) -> %% We know the module name is an atom. Msg = io_lib:format("module '~s' not found", [Mod]), unicode:characters_to_binary(Msg); %% Assertions format_error({assert_error,Obj}) -> if is_binary(Obj) -> <>; true -> Type = luerl_lib_basic:type(Obj), <<"error object is a ",Type/binary,$!>> end; %% We have an error message here already. format_error({error_message,Msg}) -> Msg; %% Error is called. format_error({error_call,Args}) -> format_error_call(Args); %% binary is passed, we treat as an error message format_error(Binary) when is_binary(Binary) -> <>; %% Everything we don't recognise or know about. format_error(Error) -> unicode:characters_to_binary(io_lib:format(<<"~w!">>, [Error])). %% format_error_call(Args) -> ErrorString. %% Just get it more or less equivalent to what Lua does. format_error_call([A|_]) when is_binary(A) -> A; format_error_call([A|_]) when is_number(A) -> iolist_to_binary(format_value(A)); format_error_call(Args) -> Type = case Args of [A|_] -> luerl_lib_basic:type(A); [] -> <<"nil">> end, <<"error object is a ",Type/binary," value">>. %% format_error(FormatString, Values) -> ErrorString. %% Useful when all the values in the list need to be formatted %% separately and will be output separately. format_error(Format, Vals) -> Ps = lists:map(fun format_value/1, Vals), unicode:characters_to_binary(io_lib:format(Format, Ps)). %% format_value(LuerlData) -> Iolist. %% Take an Luerl data and return a "printable" representation suitable %% to use when printing error messages. format_value(nil) -> <<"nil">>; format_value(true) -> <<"true">>; format_value(false) -> <<"false">>; format_value(N) when is_number(N) -> io_lib:write(N); format_value(B) when is_binary(B) -> %% A luerl string which we print with quotes around it. %% Note that the string can contain unicode codepoints. [$\',B,$\']; format_value(#tref{}) -> <<"table">>; format_value(#usdref{}) -> <<"userdata">>; format_value(#funref{}) -> <<"function">>; format_value(#erl_func{code=Fun}) -> {name,Name} = erlang:fun_info(Fun, name), atom_to_binary(Name, utf8); format_value(#erl_mfa{f=Func}) -> atom_to_binary(Func, utf8); format_value(List) when is_list(List) -> Pl = lists:map(fun format_value/1, List), lists:join($\,, Pl); %% Treat atoms as binary strings here, probably just a name. format_value(A) when is_atom(A) -> [$\',atom_to_binary(A, utf8),$\']; %% Everything else just straight through. format_value(_Other) -> <<"unknown stuff">>. %% boolean_value(Rets) -> boolean(). %% first_value(Rets) -> Value | nil. %% Test first value of return list. boolean_value([V|_]) -> ?IS_TRUE(V); boolean_value([]) -> false. first_value([V|_]) -> V; first_value([]) -> nil. %% bin_to_number(Binary) -> {ok,Number} | error. %% str_to_number(String) -> {ok,Number} | error. %% Use the scanner to process all allowed number syntaxes. bin_to_number(B) -> str_to_number(binary_to_list(B)). str_to_number(S) -> case luerl_scan:string(S) of {ok,[{'NUMERAL',_,N}],_} -> {ok,N}; {ok,[{'+',_},{'NUMERAL',_,N}],_} -> {ok,N}; {ok,[{'-',_},{'NUMERAL',_,N}],_} -> {ok,-N}; _ -> error end. number_to_list(N) -> io_lib:write(N). %% case ?IS_FLOAT_INT(N, I) of %Is it an "integer"? %% true -> integer_to_list(I); %% false -> io_lib:write(N) %% end. %% arg_to_list(Arg) -> List | 'error'. %% args_to_lists(Args) -> Lists | 'error'. %% args_to_lists(Args, Acc) -> Lists | 'error'. arg_to_list(N) when is_number(N) -> number_to_list(N); arg_to_list(B) when is_binary(B) -> binary_to_list(B); arg_to_list(_) -> error. args_to_lists(As) -> args_to_lists(As, []). args_to_lists(As, Acc) -> to_loop(As, fun arg_to_list/1, Acc). %% arg_to_number(Arg) -> Number | error. %% arg_to_number(Arg, Base) -> Number | error. %% args_to_numbers(Args) -> Numbers | 'error'. %% args_to_numbers(Arg, Arg) -> Numbers | 'error'. %% Strings always result in floats. %% Arg_to_number/2 only generates "integers". Lua does it like that. arg_to_number(N) when is_number(N) -> N; arg_to_number(B) when is_binary(B) -> case bin_to_number(B) of {ok,N} -> float(N); error -> error end; arg_to_number(_) -> error. arg_to_number(A, B) -> case conv_list([A,B], [erl_list,lua_integer]) of [N0,Base] -> case catch begin [N1] = string:tokens(N0, [9,10,11,12,13,32,160]), {ok,list_to_integer(N1, Base)} end of {ok,I} -> float(I); _ -> error end end. %% arg_to_number(A, B) -> %% case args_to_numbers([A,B]) of %% [N1,N2] when ?IS_FLOAT_INT(N1) -> %% N1 * math:pow(10,N2); %% error -> error %% end. args_to_numbers(A1, A2) -> case luerl_lib:arg_to_number(A1) of error -> error; N1 -> case luerl_lib:arg_to_number(A2) of error -> error; N2 -> [N1,N2] end end. args_to_numbers(As) -> to_loop(As, fun arg_to_number/1, []). %% arg_to_integer(Arg) -> Integer | 'error'. %% args_to_integers(Args) -> Integers | 'error'. %% args_to_integers(Arg, Arg) -> Integers | 'error'. %% Convert arguments to rounded integers. arg_to_integer(A) -> case arg_to_number(A) of N when is_integer(N) -> N; N when ?IS_FLOAT_INT(N) -> round(N); _Other -> error %Other floats are bad here end. args_to_integers(A1, A2) -> case arg_to_integer(A1) of error -> error; N1 -> case arg_to_integer(A2) of error -> error; N2 -> [N1,N2] end end. args_to_integers(As) -> to_loop(As, fun arg_to_integer/1, []). %% arg_to_float(Arg) -> Float | 'error'. %% args_to_floats(Args) -> Floats | 'error'. %% args_to_floats(Arg, Arg) -> Floats | 'error'. %% Convert arguments to rounded floats. arg_to_float(A) -> case arg_to_number(A) of N when is_integer(N) -> float(N); N when is_float(N) -> N; _Other -> error end. args_to_floats(A1, A2) -> case arg_to_float(A1) of error -> error; N1 -> case arg_to_float(A2) of error -> error; N2 -> [N1,N2] end end. args_to_floats(As) -> to_loop(As, fun arg_to_float/1, []). %% arg_to_string(Arg) -> String | error. %% arg_to_strings(Args) -> Strings | error. %% arg_to_strings(Arg, Arg) -> Strings | error. arg_to_string(N) when is_number(N) -> list_to_binary(number_to_list(N)); arg_to_string(B) when is_binary(B) -> B; arg_to_string(_) -> error. args_to_strings(As) -> args_to_strings(As, []). args_to_strings(As, Acc) -> to_loop(As, fun arg_to_string/1, Acc). %% to_loop(List, Convert, Acc) -> List | 'error'. %% Step over list using foldl and return list or 'error'. We assume %% the list won't be very long so appending is ok. to_loop([A|As], Fun, Acc) -> case Fun(A) of error -> error; %Terminate on error E -> to_loop(As, Fun, Acc ++ [E]) end; to_loop([], _Fun, Acc) -> Acc. %% conv_list(Args, ToTypes) -> List | 'error'. %% conv_list(Args, ToTypes, Done) -> List | 'error'. %% Basically a type driven foldl where we return a list or 'error'. conv_list(As, Tos) -> conv_list(As, Tos, []). conv_list(_, _, error) -> error; %Propagate error conv_list([A|As], [To|Tos], Rs) -> %% Get the right value. Ret = case To of %% Erlang types. erl_list -> arg_to_list(A); erl_string -> arg_to_list(A); %% Lua types. lua_any -> A; lua_integer -> arg_to_integer(A); lua_number -> arg_to_number(A); lua_string -> arg_to_string(A); lua_bool -> ?IS_TRUE(A) end, case Ret of error -> error; %Return error Ret -> conv_list(As, Tos, [Ret|Rs]) end; conv_list([], _, Rs) -> lists:reverse(Rs); %No more arguments, done conv_list(_, [], Rs) -> lists:reverse(Rs). %No more conversions, done %% tostring(Data, State) -> {Ret,State} | LuaError %% Convert Data to a string representation using the standard method %% taking into account both the datatype and __tostring and __name %% metakeys. tostring(Data, St) -> case luerl_heap:get_metamethod(Data, <<"__tostring">>, St) of Meta when Meta =/= nil -> tostring_meta(Data, Meta, St); nil -> case luerl_heap:get_metamethod(Data, <<"__name">>, St) of Tag when is_binary(Tag) -> {tostring_tag(Data, Tag),St}; _Other -> %Even nil here {tostring_tag(Data, false),St} end end. %% tostring_meta(Arg, Meta, St0) when ?IS_FUNCTION(Meta) -> tostring_meta(Arg, Meta, St0) -> %% We will be nice here! {[Ret|_],St1} = luerl_emul:functioncall(Meta, [Arg], St0), if is_binary(Ret) -> {Ret,St1}; is_number(Ret) -> {iolist_to_binary(io_lib:write(Ret)),St1}; true -> lua_error({illegal_return_value,tostring}, St1) end. %% tostring_tag(Data, NameTag) -> Binary. %% We ignore the NameTag for the types with untagged values. tostring_tag(nil, _Name) -> <<"nil">>; tostring_tag(false, _Name) -> <<"false">>; tostring_tag(true, _Name) -> <<"true">>; tostring_tag(N, _Name) when is_number(N) -> %% A = abs(N), %% %% Print really big/small "integers" as floats as well. %% S = if ?IS_FLOAT_INT(N), A < 1.0e14 -> %% integer_to_list(round(N)); %% true -> io_lib:write(N) %% end, iolist_to_binary(io_lib:write(N)); tostring_tag(S, _Name) when is_binary(S) -> S; tostring_tag(#tref{i=I}, Name) -> iolist_to_binary([get_tag(Name, <<"table">>),integer_to_list(I)]); tostring_tag(#usdref{i=I}, Name) -> iolist_to_binary([get_tag(Name, <<"userdata">>),integer_to_list(I)]); tostring_tag(#funref{i=I}, Name) -> %Functions defined in Lua iolist_to_binary([get_tag(Name, <<"function">>),integer_to_list(I)]); tostring_tag(#erl_func{code=C}, Name) -> %Erlang functions iolist_to_binary([get_tag(Name, <<"function">>),io_lib:write(C)]); tostring_tag(#erl_mfa{m=M,f=F}, Name) -> %Erlang MFA triplets Tag = get_tag(Name, <<"function">>), iolist_to_binary([Tag,io_lib:write_atom(M),<<":">>,io_lib:write_atom(F)]); tostring_tag(#thread{}, Name) -> get_tag(Name, <<"thread">>); tostring_tag(_, _Name) -> <<"unknown">>. get_tag(Name, Type) -> if is_binary(Name) -> <>; true -> <> end. rvirding-luerl-772b25e/src/luerl_parse.yrl0000664000232200023220000002565215114130670021232 0ustar debalancedebalance%% Copyright (c) 2013-2025 Robert Virding %% -*- mode: erlang; indent-tabs-mode: nil -*- %% %% 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. %% File : luerl_parse.yrl %% Author : Robert Virding %% Purpose : Parser for LUA 5.2. %% The Grammar rules here are taken directly from the LUA 5.2 %% manual. Unfortunately it is not an LALR(1) grammar but I have %% included a fix by Florian Weimer which makes it %% so, but it needs some after processing. Actually his fix was %% unnecessarily complex and all that was needed was to change one %% rule for statements. Expect 2. %Suppress shift/reduce warning Nonterminals chunk block stats stat semi retstat label_stat while_stat repeat_stat if_stat if_elseif if_else for_stat func_stat local_stat local_decl funcname dottedname varlist var namelist attrib attname attnamelist explist exp prefixexp args functioncall functiondef funcbody parlist tableconstructor fieldlist fields field fieldsep binop unop uminus. Terminals NAME NUMERAL LITERALSTRING 'and' 'break' 'do' 'else' 'elseif' 'end' 'false' 'for' 'function' 'goto' 'if' 'in' 'local' 'nil' 'not' 'or' 'repeat' 'return' 'then' 'true' 'until' 'while' '+' '-' '*' '/' '//' '%' '^' '&' '|' '~' '>>' '<<' '#' '==' '~=' '<=' '>=' '<' '>' '=' '(' ')' '{' '}' '[' ']' '::' ';' ':' ',' '.' '..' '...' . Rootsymbol chunk. %% uminus needed for '-' as it has duplicate precedences. Left 100 'or'. Left 200 'and'. Left 300 '<' '>' '<=' '>=' '~=' '=='. Left 400 '|'. Left 500 '~'. Left 600 '&'. Left 700 '<<' '>>'. Right 800 '..'. Left 900 '+' '-'. Left 1000 '*' '/' '//' '%'. Unary 1100 'not' '#' uminus. Right 1200 '^'. chunk -> block : '$1' . %% block ::= {stat} [retstat] block -> stats : '$1' . block -> stats retstat : '$1' ++ ['$2'] . stats -> '$empty' : [] . stats -> stats stat : '$1' ++ ['$2'] . stat -> ';' : '$1' . stat -> varlist '=' explist : {assign,line('$2'),'$1','$3'} . %% Following functioncall rule removed to stop reduce-reduce conflict. %% Replaced with a prefixexp which should give the same. We hope! %%stat -> functioncall : '$1' . stat -> prefixexp : check_functioncall('$1') . stat -> label_stat : '$1' . stat -> 'break' : {break,line('$1')} . stat -> 'goto' NAME : {goto,line('$1'),'$2'} . stat -> 'do' block 'end' : {block,line('$1'),'$2'} . stat -> while_stat : '$1' . stat -> repeat_stat : '$1' . stat -> if_stat : '$1' . stat -> for_stat : '$1' . stat -> func_stat : '$1' . stat -> local_stat : '$1' . %% stat -> local local_decl : {local,line('$1'),'$2'} . %% retstat ::= return [explist] [‘;’] retstat -> return semi : {return,line('$1'),[]} . retstat -> return explist semi : {return,line('$1'),'$2'} . semi -> ';' . %semi is never returned semi -> '$empty' . %% attnamelist ::= Name attrib {‘,’ Name attrib} attnamelist -> attname : ['$1'] . attnamelist -> attnamelist ',' attname : '$1' ++ ['$3'] . attname -> NAME : '$1'. attname -> NAME attrib : {'$1','$2'}. %% attrib ::= [‘<’ Name ‘>’] attrib -> '<' NAME '>' : {attribute,line('$2'),'$2'}. %% label ::= ‘::’ Name ‘::’ label_stat -> '::' NAME '::' : {label,line('$1'),'$2'} . while_stat -> 'while' exp 'do' block 'end' : {while,line('$1'),'$2','$4'} . repeat_stat -> 'repeat' block 'until' exp : {repeat,line('$1'),'$2','$4'} . %% stat ::= if exp then block {elseif exp then block} [else block] end if_stat -> 'if' exp 'then' block if_elseif if_else 'end' : {'if',line('$1'),[{'$2','$4'}|'$5'],'$6'} . if_elseif -> if_elseif 'elseif' exp 'then' block : '$1' ++ [{'$3','$5'}] . if_elseif -> '$empty' : [] . if_else -> 'else' block : '$2' . if_else -> '$empty' : [] . %An empty block %% stat ::= for Name '=' exp ',' exp [',' exp] do block end %% stat ::= for namelist in explist do block end for_stat -> 'for' NAME '=' explist do block end : numeric_for(line('$1'), '$2', '$4', '$6') . for_stat -> 'for' namelist 'in' explist 'do' block 'end' : generic_for(line('$1'), '$2', '$4', '$6') . %% stat ::= function funcname funcbody func_stat -> function funcname funcbody : functiondef(line('$1'),'$2','$3') . %% funcname ::= Name {'.' Name} [':' Name] funcname -> dottedname ':' NAME : dot_append(line('$2'), '$1', {method,line('$2'),'$3'}) . funcname -> dottedname : '$1' . %% stat ::= local function Name funcbody %% stat ::= local attnamelist [‘=’ explist] local_stat -> 'local' local_decl : {local,line('$1'),'$2'} . local_decl -> function NAME funcbody : functiondef(line('$1'),'$2','$3') . local_decl -> attnamelist : {assign,line(hd('$1')),'$1',[]} . local_decl -> attnamelist '=' explist : {assign,line('$2'),'$1','$3'} . dottedname -> NAME : '$1'. dottedname -> dottedname '.' NAME : dot_append(line('$2'), '$1', '$3') . %% varlist ::= var {‘,’ var} varlist -> var : ['$1'] . varlist -> varlist ',' var : '$1' ++ ['$3'] . %% var ::= Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name var -> NAME : '$1' . var -> prefixexp '[' exp ']' : dot_append(line('$2'), '$1', {key_field,line('$2'),'$3'}) . var -> prefixexp '.' NAME : dot_append(line('$2'), '$1', '$3') . %% namelist ::= Name {‘,’ Name} namelist -> NAME : ['$1'] . namelist -> namelist ',' NAME : '$1' ++ ['$3'] . %% explist ::= exp {‘,’ exp} explist -> exp : ['$1'] . explist -> explist ',' exp : '$1' ++ ['$3'] . exp -> 'nil' : '$1' . exp -> 'false' : '$1' . exp -> 'true' : '$1' . exp -> NUMERAL : '$1' . exp -> LITERALSTRING : '$1' . exp -> '...' : '$1' . exp -> functiondef : '$1' . exp -> prefixexp : '$1' . exp -> tableconstructor : '$1' . exp -> binop : '$1' . exp -> unop : '$1' . %% prefixexp ::= var | functioncall | ‘(’ exp ‘)’ prefixexp -> var : '$1' . prefixexp -> functioncall : '$1' . prefixexp -> '(' exp ')' : {single,line('$1'),'$2'} . %% functioncall ::= prefixexp args | prefixexp ‘:’ Name args functioncall -> prefixexp args : dot_append(line('$1'), '$1', {functioncall,line('$1'), '$2'}) . functioncall -> prefixexp ':' NAME args : dot_append(line('$2'), '$1', {methodcall,line('$2'),'$3','$4'}) . %% args ::= ‘(’ [explist] ‘)’ | tableconstructor | LiteralString args -> '(' ')' : [] . args -> '(' explist ')' : '$2' . args -> tableconstructor : ['$1'] . %Syntactic sugar args -> LITERALSTRING : ['$1'] . %Syntactic sugar %% functiondef ::= function funcbody functiondef -> 'function' funcbody : functiondef(line('$1'), '$2'). %% funcbody ::= ‘(’ [parlist] ‘)’ block end funcbody -> '(' ')' block 'end' : {[],'$3'} . funcbody -> '(' parlist ')' block 'end' : {'$2','$4'} . %% parlist ::= namelist [‘,’ ‘...’] | ‘... parlist -> namelist : '$1' . parlist -> namelist ',' '...' : '$1' ++ ['$3'] . parlist -> '...' : ['$1'] . %% tableconstructor ::= ‘{’ [fieldlist] ‘}’ tableconstructor -> '{' '}' : {table,line('$1'),[]} . tableconstructor -> '{' fieldlist '}' : {table,line('$1'),'$2'} . %% fieldlist ::= field {fieldsep field} [fieldsep] fieldlist -> fields : '$1' . fieldlist -> fields fieldsep : '$1' . fields -> field : ['$1'] . fields -> fields fieldsep field : '$1' ++ ['$3'] . %% field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp field -> '[' exp ']' '=' exp : {key_field,line('$1'),'$2','$5'} . field -> NAME '=' exp : {name_field,line('$1'),'$1','$3'} . field -> exp : {exp_field,line('$1'),'$1'} . fieldsep -> ',' . fieldsep -> ';' . %% fieldsep ::= ‘,’ | ‘;’ %% exp ::= exp binop exp %% exp ::= unop exp %% We have to write them these way for the prioriies to work. binop -> exp '+' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '-' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '*' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '/' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '//' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '%' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '^' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '&' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '|' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '~' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '>>' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '<<' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '==' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '~=' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '<=' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '>=' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '<' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '>' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp '..' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp 'and' exp : {op,line('$2'),cat('$2'),'$1','$3'}. binop -> exp 'or' exp : {op,line('$2'),cat('$2'),'$1','$3'}. unop -> 'not' exp : {op,line('$1'),cat('$1'),'$2'} . unop -> '#' exp : {op,line('$1'),cat('$1'),'$2'} . unop -> '~' exp : {op,line('$1'),cat('$1'),'$2'} . unop -> uminus : '$1' . uminus -> '-' exp : {op,line('$1'),'-','$2'} . Erlang code. -include("luerl.hrl"). ?MODULEDOC(false). -export([chunk/1]). %% chunk(Tokens) -> FunctionDef | Error. %% Return the parse as a callable nameless function definition. chunk(Ts) -> case parse(Ts) of {error,_}=Error -> Error; {ok,Body} -> {ok,{functiondef,1,[{'...',1}],Body}} end. cat(T) -> element(1, T). line(T) -> element(2, T). %% numeric_for(Line, LoopVar, [Init,Test,Upd], Block). numeric_for(Line, Var, [Init,Limit], Block) -> {for,Line,Var,Init,Limit,Block}; numeric_for(Line, Var, [Init,Limit,Step], Block) -> {for,Line,Var,Init,Limit,Step,Block}; numeric_for(Line, _, _, _) -> %Wrong number of expressions return_error(Line, "illegal for"). %% generic_for(Line, Names, ExpList, Block). generic_for(Line, Names, Exps, Block) -> {for,Line,Names,Exps,Block}. %% functiondef(Line, Name, {Parameters,Body}). %% functiondef(Line, {Parameters,Body}). functiondef(Line, Name, {Pars,Body}) -> {functiondef,Line,Name,Pars,Body}. functiondef(Line, {Pars,Body}) -> {functiondef,Line,Pars,Body}. %% dot_append(Line, DotList, Last) -> DotList. %% Append Last to the end of a dotlist. dot_append(Line, {'.',L,H,T}, Last) -> {'.',L,H,dot_append(Line, T, Last)}; dot_append(Line, H, Last) -> {'.',Line,H,Last}. %% check_functioncall(PrefixExp) -> PrefixExp. %% Check that the PrefixExp is a proper function call/method. check_functioncall({functioncall,_,_}=C) -> C; check_functioncall({methodcall,_,_,_}=M) -> M; check_functioncall({'.',L,H,T}) -> {'.',L,H,check_functioncall(T)}; check_functioncall(Other) -> return_error(line(Other),"illegal call"). rvirding-luerl-772b25e/src/luerl_comp.erl0000664000232200023220000002375115114130670021030 0ustar debalancedebalance%% Copyright (c) 2013-2019 Robert Virding %% %% 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. %% File : luerl_comp.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% This is the main loop of the Luerl compiler. While we can handle %% errors in this loop they should never occur as Lua basically allows %% almost everything that gets past the parser. The only exception are %% goto's to undefined labels, but we don't handle goto's yet. %% %% We also have the first pass here. It normalises the code and %% converts to an internal form. -module(luerl_comp). -export([file/1,file/2,string/1,string/2,chunk/1,chunk/2]). -export([forms/1,forms/2]). -export([debug_print/3]). -import(lists, [member/2,keysearch/3,mapfoldl/3,foreach/2]). -include_lib("kernel/include/file.hrl"). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). %% The main Lua compiler state. -record(luacomp, {base="", %Base name ldir="", %Lua file dir lfile="", %Lua file odir=".", %Output directory opts=[], %User options code=none, %Code after last pass. cinfo=none, %Common compiler info errors=[], warnings=[] }). -define(NOFILE, "-no-file-"). %% file(Name) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. %% file(Name, Options) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. file(Name) -> file(Name, [verbose,report]). file(Name, Opts) -> St0 = #luacomp{opts=Opts}, St1 = filenames(Name, St0), do_compile(file_passes(), St1). %% string(String) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. %% string(String, Options) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. string(Str) -> string(Str, [verbose,report]). string(Str, Opts) when is_binary(Str) -> string(binary_to_list(Str), Opts); string(Str, Opts) when is_list(Str) -> St0 = #luacomp{opts=Opts,code=Str}, File = prop(module, Opts, ?NOFILE), St1 = filenames(File, St0), do_compile(list_passes(), St1). %% chunk(Chunk) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. %% chunk(Chunk, Options) -> %% {ok,Chunk} | {error,Error,Warnings} | error}. chunk(Chunk) -> chunk(Chunk, [verbose,report]). chunk(Chunk, Opts) -> St0 = #luacomp{opts=Opts,code=Chunk}, File = prop(module, Opts, ?NOFILE), St1 = filenames(File, St0), do_compile(chunk_passes(), St1). %% forms(Forms) %% forms(Forms, Options) %% The deprecated fuuncttions for compiling a chunk. forms(C) -> chunk(C). forms(C, Opts) -> chunk(C, Opts). %% do_compile(Passes, CompilerState) -> %% {ok,Code} | {error,Error,Warnings} | error. do_compile(Passes, St0) -> %% The compiler state already contains the filenames. Cinfo = compiler_info(St0), %The compiler info St1 = St0#luacomp{cinfo=Cinfo}, case do_passes(Passes, St1) of {ok,St2} -> do_ok_return(St2); {error,St2} -> do_error_return(St2) end. %% filenames(File, State) -> State. %% The default output dir is the current directory unless an %% explicit one has been given in the options. filenames(?NOFILE, St) -> St#luacomp{lfile=?NOFILE}; filenames(File, St) -> Suffix = ".lua", %% Test for explicit outdir. Odir = prop(outdir, St#luacomp.opts, "."), Ldir = filename:dirname(File), Base = filename:basename(File, Suffix), Lfile = luafile(Ldir, Base, Suffix), St#luacomp{base=Base, ldir=Ldir, lfile=Lfile, odir=Odir}. luafile(".", Base, Suffix) -> Base ++ Suffix; luafile(Dir, Base, Suffix) -> filename:join(Dir, Base ++ Suffix). %% prop(Key, PropList, Default) -> Value | Default. %% Find Key, Val from PropList else Default. prop(Key, [{Key,Val}|_], _Def) -> Val; prop(Key, [_|Plist], Def) -> prop(Key, Plist, Def); prop(_Key, [], Def) -> Def. %% compiler_info(State) -> CompInfo. %% Initialise the #cinfo record passed into all compiler passes. compiler_info(#luacomp{lfile=F,opts=Opts}) -> %% The file option may get a binary so we are helpful. Vfile = iolist_to_binary(prop(file, Opts, F)), #cinfo{lfile=F,vfile=Vfile,opts=Opts}. %% file_passes() -> [Pass]. %% list_passes() -> [Pass]. %% chunk_passes() -> [Pass]. %% Build list of passes. file_passes() -> %Reading from file [{do,fun do_scan_file/1}, {when_flag,to_scan,{done,fun(St) -> {ok,St} end}}, {do,fun do_parse/1} | chunk_passes()]. list_passes() -> %Scanning string [{do,fun do_scan_string/1}, {when_flag,to_scan,{done,fun(St) -> {ok,St} end}}, {do,fun do_parse/1} | chunk_passes()]. chunk_passes() -> %Doing the chunk [{when_flag,to_parse,{done,fun(St) -> {ok,St} end}}, {do,fun do_init_comp/1}, {do,fun do_comp_normalise/1}, {when_flag,to_norm,{done,fun(St) -> {ok,St} end}}, {do,fun do_comp_lint/1}, {do,fun do_comp_vars/1}, {when_flag,to_vars,{done,fun(St) -> {ok,St} end}}, %% {do,fun do_comp_locf/1}, {do,fun do_comp_env/1}, {when_flag,to_env,{done,fun(St) -> {ok,St} end}}, {do,fun do_code_gen/1}, {unless_flag,no_iopt,{do,fun do_peep_op/1}}]. %% do_passes(Passes, State) -> {ok,State} | {error,Reason}. %% Interpret the list of commands in a pass. %% %% Commands can be one of: %% %% {when_flag,Flag,Cmd} %% {unless_flag,Flag,Cmd} %% {do,Fun} %% {done,PrintFun,Ext} do_passes([{do,Fun}|Ps], St0) -> case Fun(St0) of {ok,St1} -> do_passes(Ps, St1); {error,St1} -> {error,St1} end; do_passes([{when_flag,Flag,Cmd}|Ps], St) -> case member(Flag, St#luacomp.opts) of true -> do_passes([Cmd|Ps], St); false -> do_passes(Ps, St) end; do_passes([{unless_flag,Flag,Cmd}|Ps], St) -> case member(Flag, St#luacomp.opts) of true -> do_passes(Ps, St); false -> do_passes([Cmd|Ps], St) end; do_passes([{done,Fun}|_], St) -> Fun(St); do_passes([], St) -> {ok,St}. %% do_scan_file(State) -> {ok,State} | {error,State}. %% do_scan_string(State) -> {ok,State} | {error,State}. %% do_parse(State) -> {ok,State} | {error,State}. %% do_init_comp(State) -> {ok,State} | {error,State}. %% do_comp_normalise(State) -> {ok,State} | {error,State}. %% do_comp_lint(State) -> {ok,State} | {error,State}. %% do_comp_vars(State) -> {ok,State} | {error,State}. %% do_comp_env(State) -> {ok,State} | {error,State}. %% do_comp_cg(State) -> {ok,State} | {error,State}. %% do_comp_peep(State) -> {ok,State} | {error,State}. %% The actual compiler passes. do_scan_file(#luacomp{lfile=Name,opts=Opts}=St) -> case luerl_io:scan_file(Name, 1) of {ok,Ts} -> debug_print(Opts, "scan: ~p\n", [Ts]), {ok,St#luacomp{code=Ts}}; {eof,_} -> {ok,St#luacomp{code=[]}}; {error,E} -> {error,St#luacomp{errors=[E]}} end. do_scan_string(#luacomp{code=Str,opts=Opts}=St) -> case luerl_scan:string(Str) of {ok,Ts,_} -> debug_print(Opts, "scan: ~p\n", [Ts]), {ok,St#luacomp{code=Ts}}; {error,E,_} -> {error,St#luacomp{errors=[E]}} end. do_parse(#luacomp{code=Ts,opts=Opts}=St) -> case luerl_parse:chunk(Ts) of {ok,Chunk} -> debug_print(Opts, "parse: ~p\n", [Chunk]), {ok,St#luacomp{code=Chunk}}; {error,E} -> {error,St#luacomp{errors=[E]}} end. do_init_comp(#luacomp{}=St) -> %% Nothing to do here now. {ok,St}. do_comp_normalise(#luacomp{code=Code0,cinfo=Cinfo}=St) -> {ok,Code1} = luerl_comp_normalise:chunk(Code0, Cinfo), {ok,St#luacomp{code=Code1}}. do_comp_lint(#luacomp{code=Code,cinfo=Cinfo}=St) -> case luerl_comp_lint:chunk(Code, Cinfo) of {ok,Ws} -> {ok,St#luacomp{warnings=Ws}}; {error,Es,Ws} -> {error,St#luacomp{errors=Es,warnings=Ws}} end. do_comp_vars(#luacomp{code=Code0,cinfo=Cinfo}=St) -> {ok,Code1} = luerl_comp_vars:chunk(Code0, Cinfo), {ok,St#luacomp{code=Code1}}. %% do_comp_locf(#luacomp{code=Code0,cinfo=Cinfo}=St) -> %% case luerl_comp_locf:chunk(Code0, Cinfo) of %% {ok,Code1} -> {ok,St#luacomp{code=Code1}}; %% {ok,Code1,Ws} -> {ok,St#luacomp{code=Code1,warnings=Ws}}; %% {error,Es} -> {error,St#luacomp{errors=Es}} %% end. do_comp_env(#luacomp{code=Code0,cinfo=Cinfo}=St) -> {ok,Code1} = luerl_comp_env:chunk(Code0, Cinfo), {ok,St#luacomp{code=Code1}}. do_code_gen(#luacomp{code=Code0,cinfo=Cinfo}=St) -> {ok,Code1} = luerl_comp_cg:chunk(Code0, Cinfo), {ok,St#luacomp{code=Code1}}. do_peep_op(#luacomp{code=Code0,cinfo=Cinfo}=St) -> {ok,Code1} = luerl_comp_peep:chunk(Code0, Cinfo), {ok,St#luacomp{code=Code1}}. do_ok_return(#luacomp{lfile=Lfile,opts=Opts,code=C,warnings=Ws}) -> Report = lists:member(report, Opts), ?IF(Report, list_warnings(Lfile, Ws), ok), {ok,C}. do_error_return(#luacomp{lfile=Lfile,opts=Opts,errors=Es,warnings=Ws}) -> Report = lists:member(report, Opts), Return = lists:member(return, Opts), ?IF(Report, begin list_errors(Lfile, Es), list_warnings(Lfile, Ws) end, ok), ?IF(Return, {error,Es,Ws}, error). debug_print(Opts, Format, Args) -> ?DEBUG_PRINT(Format, Args, Opts). list_warnings(F, Ws) -> foreach(fun ({Line,Mod,Warn}) -> Cs = Mod:format_error(Warn), io:format("~s:~w: Warning: ~s\n", [F,Line,Cs]); ({Mod,Warn}) -> Cs = Mod:format_error(Warn), io:format("~s: Warning: ~s\n", [F,Cs]) end, Ws). list_errors(F, Es) -> foreach(fun ({Line,Mod,Error}) -> Cs = Mod:format_error(Error), io:format("~s:~w: ~s\n", [F,Line,Cs]); ({Mod,Error}) -> Cs = Mod:format_error(Error), io:format("~s: ~s\n", [F,Cs]) end, Es). rvirding-luerl-772b25e/src/luerl_anno.erl0000664000232200023220000000507615114130670021025 0ustar debalancedebalance%% Copyright (c) 2019 Robert Virding %% %% 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. %% File : luerl_anno.erl %% Author : Robert Virding %% Purpose : Handle annotations in the Luerl abstract code. %% We keep the same standard as in the Erlang AST: %% %% - annotations with just the line number can be just the integer. %% - in an aonnotation list the line number is just an integer while %% all other annotations have the format {Key,Value}. -module(luerl_anno). -include("luerl.hrl"). ?MODULEDOC(false). -export([new/0,new/1,new/2,set_line/2,line/1,set/3,get/2]). %% new() -> Anno. %% new(Line) -> Anno. %% new(Key, Val) -> Anno. %% Create an empty annotation, one containing Line and one containing %% a general Key/Val. new() -> []. new(Line) -> Line. new(Key, Val) -> set(Key, Val, new()). %% set_line(Line, Anno) -> Anno. %% line(Anno) -> Line | undefined. %% Specific functions for accessing line numbers in the anno. set_line(Line, Anno) when is_integer(Anno) -> Line; set_line(Line, Anno) -> set_line1(Line, Anno). set_line1(Line, [Old|Anno]) when is_integer(Old) -> [Line|Anno]; set_line1(Line, [A|Anno]) -> [A|set_line1(Line, Anno)]; set_line1(Line, []) -> [Line]. line(Anno) when is_integer(Anno) -> Anno; line(Anno) -> line1(Anno). line1([Line|_]) when is_integer(Line) -> Line; line1([_|Anno]) -> line1(Anno); line1([]) -> undefined. %% set(Key, Value, Anno) -> Anno. %% get(Key, Anno) -> Value | undefined. %% Generic accessing functions for the anno. set(line, Val, Anno) -> set_line(Val, Anno); set(Key, Val, Anno) when is_integer(Anno) -> [Anno,{Key,Val}]; set(Key, Val, Anno) -> set1(Key, Val, Anno). set1(Key, Val, [{Key,_Old}|Anno]) -> [{Key,Val}|Anno]; set1(Key, Val, [A|Anno]) -> [A|set1(Key, Val, Anno)]; set1(Key, Val, []) -> [{Key,Val}]. get(line, Anno) -> line(Anno); %This is untagged get(_Key, Anno) when is_integer(Anno) -> %This is untagged so not Key undefined; get(Key, Anno) -> get1(Key, Anno). get1(Key, [{Key,Val}|_Anno]) -> Val; get1(Key, [_|Anno]) -> get1(Key, Anno); get1(_Key, []) -> undefined. rvirding-luerl-772b25e/src/luerl_lib_utf8.erl0000664000232200023220000001151715114130670021603 0ustar debalancedebalance%% Copyright (c) 2013-2020 Robert Virding %% %% 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. %% File : luerl_lib_utf8.erl %% Author : Robert Virding %% Purpose : The utf8 library for Luerl. -module(luerl_lib_utf8). -include("luerl.hrl"). ?MODULEDOC(false). -export([install/1,utf8_char/3,codes/3,codepoint/3,utf8_len/3,offset/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten these install(St) -> luerl_heap:alloc_table(table(), St). table() -> [{<<"char">>,#erl_mfa{m=?MODULE,f=utf8_char}}, {<<"charpattern">>,<<"[\0-\x7F\xC2-\xF4][\x80-\xBF]*">>}, {<<"codes">>,#erl_mfa{m=?MODULE,f=codes}}, {<<"codepoint">>,#erl_mfa{m=?MODULE,f=codepoint}}, {<<"len">>,#erl_mfa{m=?MODULE,f=utf8_len}}, {<<"offset">>,#erl_mfa{m=?MODULE,f=offset}} ]. %% char(...) -> String. %% Receives zero or more integers, converts each one to its %% corresponding UTF-8 byte sequence and returns a string with the %% concatenation of all these sequences. utf8_char(_, As, St) -> case luerl_lib:args_to_integers(As) of Is when is_list(Is) -> Ss = << <> || I <- Is >>, {[Ss],St}; error -> badarg_error(char, As, St) end. %% len(...) -> Integer. %% Returns the number of UTF-8 characters in string s that start %% between positions i and j (both inclusive). The default for i is 1 %% and for j is -1. If it finds any invalid byte sequence, returns a %% false value plus the position of the first invalid byte. utf8_len(_, As, St) -> {Str,I,J} = string_args(As, len, St), StrLen = byte_size(Str), Ret = if I > J -> [0]; %Do the same as Lua true -> Bin = binary_part(Str, I - 1, StrLen - I + 1), case bin_len(Bin, StrLen - J, 0) of {ok,Size} -> [Size]; {error,Rest} -> [nil,StrLen - byte_size(Rest) + 1] end end, {Ret,St}. bin_len(Bin, Last, N) when byte_size(Bin) =< Last -> {ok,N}; bin_len(Bin0, Last, N) -> try <<_/utf8,Bin1/binary>> = Bin0, bin_len(Bin1, Last, N+1) catch _:_ -> {error,Bin0} end. %% codepoint(...) -> [Integer]. %% Returns the codepoints (as integers) from all characters in s that %% start between byte position i and j (both included). The default %% for i is 1 and for j is i. It raises an error if it meets any %% invalid byte sequence. codepoint(_, As, St) -> {Str,I,J} = string_args(As, codepoint, St), StrLen = byte_size(Str), Ret = if I > J -> []; %Do the same as Lua true -> Bin = binary_part(Str, I - 1, StrLen - I + 1), case bin_codepoint(Bin, StrLen - J, []) of {ok,Cps} -> Cps; {error,_} -> badarg_error(codepoint, As, St) end end, {Ret,St}. bin_codepoint(Bin, Last, Cps) when byte_size(Bin) =< Last -> {ok,lists:reverse(Cps)}; bin_codepoint(Bin0, Last, Cps) -> try <> = Bin0, bin_codepoint(Bin1, Last, [C|Cps]) catch _:_ -> {error,Bin0} end. %% codes(String) -> [Fun,String,P]. codes(_, As, St) -> case luerl_lib:conv_list(As, [lua_string]) of error -> badarg_error(codes, As, St); [Str|_] -> {[#erl_func{code=fun codes_next/2},Str,0],St} end. codes_next([A], St) -> codes_next([A,0], St); codes_next([Str,P|_], St) when byte_size(Str) =< P -> {[nil],St}; codes_next([Str,P|_], St) when is_binary(Str) -> <<_:P/binary,C/utf8,Rest/binary>> = Str, P1 = byte_size(Str) - byte_size(Rest), {[P1,C],St}. %% offset(String, N, ...) -> Integer. -spec offset(_, [_], any()) -> no_return(). offset(_, As, St) -> _ = string_args(As, offset, St), %% We don't do anything yet. lua_error({'NYI',offset}, St). %% string_args(Args, Op, St) -> {String,I,J}. %% Return the string, i and j values from the arguments. Generate a %% badarg error on bad values. string_args(As, Op, St) -> %% Get the args. Args = luerl_lib:conv_list(As, [lua_string,lua_integer,lua_integer]), case Args of %Cunning here, export A1,A2,A3 [A1,A2,A3|_] -> ok; [A1,A2] -> A3 = byte_size(A1); [A1] -> A2 = 1, A3 = byte_size(A1); error -> A1 = A2 = A3 = ok, badarg_error(Op, As, St) end, StrLen = byte_size(A1), %% Check args and return Str, I, J. Str = A1, I = if A2 > 0, A2 =< StrLen -> A2; A2 < 0, A2 >= -StrLen -> StrLen + A2 + 1; true -> badarg_error(Op, As, St) end, J = if A3 > 0, A3 =< StrLen -> A3; A3 < 0, A3 >= -StrLen -> StrLen + A3 + 1; true -> badarg_error(Op, As, St) end, {Str,I,J}. rvirding-luerl-772b25e/src/luerl_comp_env.erl0000664000232200023220000003001715114130670021671 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : luerl_comp_env.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Does variable and stack analysis in the compiler -module(luerl_comp_env). -include("luerl.hrl"). -include("luerl_comp.hrl"). ?MODULEDOC(false). -export([chunk/2]). -import(ordsets, [is_element/2,intersection/2,subtract/2]). %% Local state. -record(c_env, {lfs=[], %Variable frames efs=[], %Environment frames vars=none, fs=[], locv=false, %Local variables locf %Local frame }). %% chunk(Code, CompInfo) -> {ok,Code}. chunk(Code0, #cinfo{opts=Opts}=_Ci) -> St0 = #c_env{}, %Local state {Code1,_} = functiondef(Code0, St0), luerl_comp:debug_print(Opts, "ce: ~p\n", [Code1]), {ok,Code1}. %% alloc_frame(State) -> State. %% pop_frame(State) -> State. %% get_frame(State) -> Frame. alloc_frame(#c_env{vars=#vars{},fs=Fs}=St) -> F = new_frame(), St#c_env{fs=[F|Fs]}. pop_frame(#c_env{fs=[_|Fs]}=St) -> St#c_env{fs=Fs}. get_frame(#c_env{fs=[F|_]}) -> F. %% new_frame(LocalSize, EnvSize) -> Frame. %% We know frame will be tuples which we index from 1. Also Lua has %% the feature that every time you add a local variable you get a new %% version of it which shadows the old one. We handle this by keeping %% them in reverse order and always pushing variable to front of %% list. %% %% We get the size from the index of the last variable of each type added. %% %% NOTE: We can have empty frames here. The emulator knows about this %% and can handle it. %% %% Frame :: {LocalIndex,EnvIndex,Vars} %% Var :: {Name,Type,Index} new_frame() -> {0,0,[]}. find_frame_var(N, {_,_,Fs}) -> find_frame_var_1(N, Fs). find_frame_var_1(N, [{N,Type,I}|_]) -> {yes,Type,I}; find_frame_var_1(N, [_|F]) -> find_frame_var_1(N, F); find_frame_var_1(_, []) -> no. frame_local_size({Li,_,_}) -> Li. frame_env_size({_,Ei,_}) -> Ei. add_frame_local_var(N, {Li,Ei,Fs}) -> {Li+1,Ei,[{N,lvar,Li+1}|Fs]}. add_frame_env_var(N, {Li,Ei,Fs}) -> {Li,Ei+1,[{N,evar,Ei+1}|Fs]}. %% find_fs_var(Name, FrameStack) -> {yes,Type,Depth,Index} | no. %% Find a variable in the frame stack returning its depth and %% index. find_fs_var(N, Fs) -> find_fs_var(N, Fs, 1, 1). find_fs_var(N, [F|Fs], Ld, Ed) -> case find_frame_var(N, F) of {yes,lvar,Li} -> {yes,lvar,Ld,Li}; {yes,evar,Ei} -> {yes,evar,Ed,Ei}; no -> Ld1 = Ld + 1, Ed1 = Ed + 1, find_fs_var(N, Fs, Ld1, Ed1) end; find_fs_var(_, [], _, _) -> no. %% add_var(Var, State) -> State. %% get_var(Var, State) -> #lvar{} | #evar{} | #gvar{}. add_var(#var{name=N}, St) -> case var_type(N, St) of local -> add_local_var(N, St); env -> add_env_var(N, St) end. add_env_var(V, #c_env{fs=[F0|Fs]}=St) -> F1 = add_frame_env_var(V, F0), St#c_env{fs=[F1|Fs]}. add_local_var(N, #c_env{fs=[F0|Fs]}=St) -> F1 = add_frame_local_var(N, F0), St#c_env{fs=[F1|Fs]}. get_var(#var{l=Line,name=N}, #c_env{fs=Fs}) -> case find_fs_var(N, Fs) of {yes,lvar,Ld,Li} -> #lvar{l=Line,n=N,d=Ld,i=Li}; {yes,evar,Ed,Ei} -> #evar{l=Line,n=N,d=Ed,i=Ei}; no -> #gvar{l=Line,n=N} end. var_type(N, #c_env{vars=#vars{fused=Fused}}) -> case is_element(N, Fused) of true -> env; false -> local end. %% stmt(Stmts, State) -> {Stmts,State}. stmts([S0|Ss0], St0) -> {S1,St1} = stmt(S0, nul, St0), %% io:format("ss1: ~p\n", [{Loc0,Free0,Used0}]), {Ss1,St2} = stmts(Ss0, St1), {[S1|Ss1],St2}; stmts([], St) -> {[],St}. %% stmt(Stmt, State) -> {Stmt,State}. stmt(#assign_stmt{}=A, _, St) -> assign_stmt(A, St); stmt(#call_stmt{}=C, _, St) -> call_stmt(C, St); stmt(#return_stmt{}=R, _, St) -> return_stmt(R, St); stmt(#break_stmt{}=B, _, St) -> {B,St}; stmt(#block_stmt{}=B, _, St) -> block_stmt(B, St); stmt(#while_stmt{}=W, _, St) -> while_stmt(W, St); stmt(#repeat_stmt{}=R, _, St) -> repeat_stmt(R, St); stmt(#if_stmt{}=I, _, St) -> if_stmt(I, St); stmt(#nfor_stmt{}=F, _, St) -> numfor_stmt(F, St); stmt(#gfor_stmt{}=F, _, St) -> genfor_stmt(F, St); stmt(#local_assign_stmt{}=L, _, St) -> local_assign_stmt(L, St); stmt(#local_fdef_stmt{}=L, _, St) -> local_fdef_stmt(L, St); stmt(#expr_stmt{}=E, _, St) -> expr_stmt(E, St). %% assign_stmt(Assign, State) -> {Assign,State}. assign_stmt(#assign_stmt{vars=Vs0,exps=Es0}=A, St0) -> {Vs1,St1} = assign_loop(Vs0, St0), {Es1,St2} = explist(Es0, St1), {A#assign_stmt{vars=Vs1,exps=Es1},St2}. assign_loop([V0|Vs0], St0) -> {V1,St1} = var(V0, St0), {Vs1,St2} = assign_loop(Vs0, St1), {[V1|Vs1],St2}; assign_loop([], St) -> {[],St}. var(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,St1} = prefixexp_first(Exp0, St0), {Rest1,St2} = var_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},St2}; var(#var{}=V0, St) -> V1 = get_var(V0, St), {V1,St}. var_rest(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,St1} = prefixexp_element(Exp0, St0), {Rest1,St2} = var_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},St2}; var_rest(Exp, St) -> var_last(Exp, St). var_last(#key{key=Exp0}=K, St0) -> {Exp1,St1} = exp(Exp0, St0), {K#key{key=Exp1},St1}. %% call_stmt(Call, State) -> {Call,State}. call_stmt(#call_stmt{call=Exp0}=C, St0) -> {Exp1,St1} = exp(Exp0, St0), {C#call_stmt{call=Exp1},St1}. %% return_stmt(Return, State) -> {Return,State}. return_stmt(#return_stmt{exps=Es0}=R, St0) -> {Es1,St1} = explist(Es0, St0), {R#return_stmt{exps=Es1},St1}. %% block_stmt(Block, State) -> {Block,State}. block_stmt(#block_stmt{body=Ss0,vars=Vars}=B, St0) -> Do = fun(S) -> stmts(Ss0, S) end, {Ss1,Fr,St1} = with_block(Do, Vars, St0), Lsz = frame_local_size(Fr), Esz = frame_env_size(Fr), {B#block_stmt{body=Ss1,lsz=Lsz,esz=Esz},St1}. %% do_block(Block, State) -> {Block,State}. do_block(#block{body=Ss0,vars=Vars}=B, St0) -> Do = fun(S) -> stmts(Ss0, S) end, {Ss1,Fr,St1} = with_block(Do, Vars, St0), Lsz = frame_local_size(Fr), Esz = frame_env_size(Fr), {B#block{body=Ss1,lsz=Lsz,esz=Esz},St1}. %% with_block(Do, Vars, State) -> {Ret,State}. %% with_block(Do, Env, Vars, State) -> {Ret,State}. %% Do a block initialising/clearing frames. We always push a local %% frame even if it not used. with_block(Do, Vars, #c_env{vars=OldVars}=St0) -> St1 = alloc_frame(St0#c_env{vars=Vars}), {Ret,St2} = Do(St1), Fr = get_frame(St2), St3 = pop_frame(St2), {Ret,Fr,St3#c_env{vars=OldVars}}. %% while_stmt(While, State) -> {While,State}. while_stmt(#while_stmt{exp=E0,body=B0}=W, St0) -> {E1,St1} = exp(E0, St0), {B1,St2} = do_block(B0, St1), {W#while_stmt{exp=E1,body=B1},St2}. %% repeat_stmt(Repeat, State) -> {Repeat,State}. repeat_stmt(#repeat_stmt{body=B0}=R, St0) -> {B1,St1} = do_block(B0, St0), {R#repeat_stmt{body=B1},St1}. %% if_stmt(If, State) -> {If,State}. if_stmt(#if_stmt{tests=Ts0,else_block=E0}=I, St0) -> {Ts1,St1} = if_tests(Ts0, St0), {E1,St2} = do_block(E0, St1), {I#if_stmt{tests=Ts1,else_block=E1},St2}. if_tests([{E0,B0}|Ts0], St0) -> {E1,St1} = exp(E0, St0), {B1,St2} = do_block(B0, St1), {Ts1,St3} = if_tests(Ts0, St2), {[{E1,B1}|Ts1],St3}; if_tests([], St) -> {[],St}. %% numfor_stmt(For, State) -> {For,State}. numfor_stmt(#nfor_stmt{var=V0,init=I0,limit=L0,step=S0,body=B0}=F, St0) -> {[I1,L1,S1],St1} = explist([I0,L0,S0], St0), {[V1],B1,St2} = for_block([V0], B0, St1), {F#nfor_stmt{var=V1,init=I1,limit=L1,step=S1,body=B1},St2}. %% genfor_stmt(For, State) -> {For,State}. genfor_stmt(#gfor_stmt{vars=Vs0,gens=Gs0,body=B0}=F, St0) -> {Gs1,St1} = explist(Gs0, St0), {Vs1,B1,St2} = for_block(Vs0, B0, St1), {F#gfor_stmt{vars=Vs1,gens=Gs1,body=B1},St2}. for_block(Vs0, #block{body=Ss0,vars=Vars}=B, St0) -> Do = fun (S0) -> Fun = fun (V, Sa) -> Sb = add_var(V, Sa), {get_var(V, Sb),Sb} end, {Vs1,S1} = lists:mapfoldl(Fun, S0, Vs0), {Ss1,S2} = stmts(Ss0, S1), {{Vs1,Ss1},S2} end, {{Vs1,Ss1},Fr,St1} = with_block(Do, Vars, St0), Lsz = frame_local_size(Fr), Esz = frame_env_size(Fr), {Vs1,B#block{body=Ss1,lsz=Lsz,esz=Esz},St1}. %% local_assign_stmt(Local, State) -> {Local,State}. local_assign_stmt(#local_assign_stmt{vars=Vs0,exps=Es0}=L, St0) -> %% io:fwrite("las: ~p\n", [{Es0,St0}]), {Es1,St1} = explist(Es0, St0), %% io:fwrite("las> ~p\n", [{Es1,St1}]), AddVar = fun (V, S0) -> S1 = add_var(V, S0), {get_var(V, S1),S1} end, {Vs1,St2} = lists:mapfoldl(AddVar, St1, Vs0), %% io:fwrite("las> ~p\n", [{Vs1,St2}]), {L#local_assign_stmt{vars=Vs1,exps=Es1},St2}. %% local_fdef_stmt(Local, State) -> {Local,State}. %% Add function name first in case of recursive call. local_fdef_stmt(#local_fdef_stmt{var=V,func=F0}=L, St0) -> St1 = add_var(V, St0), {F1,St2} = functiondef(F0, St1), V1 = get_var(V, St2), %% io:fwrite("lf: ~p\n", [St0]), %% io:fwrite("lf: ~p\n", [St1]), %% io:fwrite("lf: ~p\n", [St2]), {L#local_fdef_stmt{var=V1,func=F1},St2}. %% expr_stmt(Expr, State) -> {Call,State}. %% The expression pseudo statement. This will return a single value. expr_stmt(#expr_stmt{exp=Exp0}=E, St0) -> {Exp1,St1} = exp(Exp0, St0), {E#expr_stmt{exp=Exp1},St1}. %% explist(Exprs, State) -> {Exprs,State}. %% exp(Expr, State) -> {Expr,State}. %% prefixexp(Expr, State) -> {Expr,State}. explist([E0|Es0], St0) -> {E1,St1} = exp(E0, St0), {Es1,St2} = explist(Es0, St1), {[E1|Es1],St2}; explist([], St) -> {[],St}. %No expressions at all exp(#lit{}=L, St) -> {L,St}; %Nothing to do exp(#fdef{}=F, St) -> functiondef(F, St); exp(#op{args=Es0}=Op, St0) -> {Es1,St1} = explist(Es0, St0), {Op#op{args=Es1},St1}; exp(#tabcon{fields=Fs0}=T, St0) -> {Fs1,St1} = tableconstructor(Fs0, St0), {T#tabcon{fields=Fs1},St1}; exp(E, St) -> prefixexp(E, St). prefixexp(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,St1} = prefixexp_first(Exp0, St0), {Rest1,St2} = prefixexp_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},St2}; prefixexp(Exp, St) -> prefixexp_first(Exp, St). prefixexp_first(#single{exp=E0}=S, St0) -> {E1,St1} = exp(E0, St0), {S#single{exp=E1},St1}; prefixexp_first(#var{}=V0, St) -> V1 = get_var(V0, St), {V1,St}. prefixexp_rest(#dot{exp=Exp0,rest=Rest0}=D, St0) -> {Exp1,St1} = prefixexp_element(Exp0, St0), {Rest1,St2} = prefixexp_rest(Rest0, St1), {D#dot{exp=Exp1,rest=Rest1},St2}; prefixexp_rest(Exp, St) -> prefixexp_element(Exp, St). prefixexp_element(#key{key=E0}=K, St0) -> {E1,St1} = exp(E0, St0), {K#key{key=E1},St1}; prefixexp_element(#fcall{args=As0}=F, St0) -> {As1,St1} = explist(As0, St0), {F#fcall{args=As1},St1}; prefixexp_element(#mcall{args=As0}=M, St0) -> {As1,St1} = explist(As0, St0), {M#mcall{args=As1},St1}. %% functiondef(Func, State) -> {Func,State}. functiondef(#fdef{pars=Ps0,body=Ss0,vars=Vars}=F, St0) -> Do = fun (S0) -> Fun = fun (V, Sa) -> Sb = add_var(V, Sa), {get_var(V, Sb),Sb} end, {Ps1,S1} = lists:mapfoldl(Fun, S0, Ps0), {Ss1,S2} = stmts(Ss0, S1), {{Ps1,Ss1},S2} end, {{Ps1,Ss1},Fr,St1} = with_block(Do, Vars, St0), Lsz = frame_local_size(Fr), Esz = frame_env_size(Fr), {F#fdef{pars=Ps1,body=Ss1,lsz=Lsz,esz=Esz},St1}. %% tableconstructor(Fields, State) -> {Fields,State}. tableconstructor(Fs0, St0) -> Fun = fun (#efield{val=V0}=F, S0) -> {V1,S1} = exp(V0, S0), {F#efield{val=V1},S1}; (#kfield{key=K0,val=V0}=F, S0) -> {K1,S1} = exp(K0, S0), {V1,S2} = exp(V0, S1), {F#kfield{key=K1,val=V1},S2} end, {Fs1,St1} = lists:mapfoldl(Fun, St0, Fs0), {Fs1,St1}. rvirding-luerl-772b25e/src/Elixir.Luerl.erl0000664000232200023220000001747015114130670021206 0ustar debalancedebalance%% Copyright (c) 2013-2024 Robert Virding %% %% 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. %% File : luerl_ex.erl %% Authors : Cees de Groot %% Purpose : Elixir-style wrappers for luerl.erl %% This module just contains functions that forward to luerl.erl, but place %% the VM State arguments in the first position rather than the last. This %% better matches Elixir conventions and allows for using the Elixir pipe %% operator '|>' to chain Luerl function calls. -module('Elixir.Luerl'). -include("luerl.hrl"). ?MODULEDOC(""" Elixir API for Luerl, an implementation of Lua 5.3 written in Erlang. This module provides an idiomatic Elixir interface to the Luerl Lua interpreter with state as the first argument for better pipe operator usage. """). %% Basic user API to luerl. -export([init/0,gc/1, load/2,load/3,loadfile/2,loadfile/3, path_loadfile/2,path_loadfile/3,path_loadfile/4, load_module/3,load_module_dec/3, do/2,do_dec/2,do/3,do_dec/3, dofile/2,dofile/3,dofile_dec/2,dofile_dec/3, call/3,call_chunk/2,call_chunk/3, call_function/3,call_function_enc/3,call_function_dec/3, call_method/4,call_method_dec/4, get_table_keys/2,get_table_keys_dec/2, set_table_keys/3,set_table_keys_dec/3, get_table_key/3,set_table_key/4, get_stacktrace/1 ]). %% Tracing. -export([set_trace_func/2,clear_trace_func/1, set_trace_data/2,get_trace_data/1]). %% Encoding and decoding. -export([encode/2,encode_list/2,decode/2,decode_list/2]). %%Helping with storing VM state -export([externalize/1,internalize/1]). %% Storing and retrieving private data -export([put_private/3,get_private/2,delete_private/2]). ?DOC( #{equiv => luerl:init()} ). init() -> luerl:init(). ?DOC( #{equiv => luerl:gc(LuaState)} ). gc(LuaState) -> luerl:gc(LuaState). ?DOC( #{equiv => luerl:set_trace_func(Function, LuaState), group => <<"Trace Control functions">>} ). set_trace_func(LuaState, Function) -> luerl:set_trace_func(Function, LuaState). ?DOC( #{equiv => luerl:clear_trace_func(LuaState), group => <<"Trace Control functions">>} ). clear_trace_func(LuaState) -> luerl:clear_trace_func(LuaState). ?DOC( #{equiv => luerl:get_trace_func(LuaState), group => <<"Trace Control functions">>} ). get_trace_data(LuaState) -> luerl:get_trace_data(LuaState). ?DOC( #{equiv => luerl:set_trace_func(TraceData, LuaState), group => <<"Trace Control functions">>} ). set_trace_data(LuaState, TraceData) -> luerl:set_trace_data(TraceData, LuaState). ?DOC( #{equiv => luerl:load(Chunk, LuaState)} ). load(LuaState, Chunk) -> luerl:load(LuaState, Chunk). ?DOC( #{equiv => luerl:load(Chunk, CompileOptions, LuaState)} ). load(LuaState, Chunk, CompileOptions) -> luerl:load(Chunk, CompileOptions, LuaState). ?DOC( #{equiv => luerl:loadfile(FileName, LuaState)} ). loadfile(LuaState, FileName) -> luerl:loadfile(FileName, LuaState). ?DOC( #{equiv => luerl:loadfile(FileName, CompileOptions, LuaState)} ). loadfile(LuaState, FileName, CompileOptions) -> luerl:loadfile(FileName, CompileOptions, LuaState). ?DOC( #{equiv => luerl:path_loadfile(FileName, LuaState)} ). path_loadfile(LuaState, FileName) -> luerl:path_loadfile(FileName, LuaState). ?DOC( #{equiv => luerl:path_loadfile(Path, FileName, LuaState)} ). path_loadfile(LuaState, Path, FileName) -> luerl:path_loadfile(Path, FileName, LuaState). ?DOC( #{equiv => luerl:path_loadfile(Path, FileName, CompileOptions, LuaState)} ). path_loadfile(LuaState, Path, FileName, CompileOptions) -> luerl:path_loadfile(Path, FileName, CompileOptions, LuaState). ?DOC( #{equiv => luerl:load_module(KeyPath, ErlangModule, LuaState)} ). load_module(LuaState, KeyPath, ErlangModule) -> luerl:load_module(KeyPath, ErlangModule, LuaState). ?DOC( #{equiv => luerl:load_module_dec(KeyPath, ErlangModule, LuaState)} ). load_module_dec(LuaState, KeyPath, ErlangModule) -> luerl:load_module_dec(KeyPath, ErlangModule, LuaState). do(St, S) -> luerl:do(S, St). do(St, S, Opts) -> luerl:do(S, Opts, St). do_dec(St, S) -> luerl:do_dec(S, St). do_dec(St, S, Opts) -> luerl:do_dec(S, Opts, St). dofile(St, Path) -> luerl:dofile(Path, St). dofile(St, Path, Opts) -> luerl:dofile(Path, Opts, St). dofile_dec(St, Path) -> luerl:dofile_dec(Path, St). dofile_dec(St, Path, Opts) -> luerl:dofile_dec(Path, Opts, St). call(St, C, Args) -> luerl:call(C, Args, St). call_chunk(St, C) -> luerl:call_chunk(C, St). call_chunk(St, C, Args) -> luerl:call_chunk(C, Args, St). call_function(St, Fp, Args) -> luerl:call_function(Fp, Args, St). call_function_enc(St, Dfunc, Dargs) -> luerl:call_function_enc(Dfunc, Dargs, St). call_function_dec(St, Dfunc, Dargs) -> luerl:call_function_dec(Dfunc, Dargs, St). call_method(St, Obj, Meth, Args) -> luerl:call_method(Obj, Meth, Args, St). call_method_dec(St, Dobj, Dmeth, Dargs) -> luerl:call_method_dec(Dobj, Dmeth, Dargs, St). get_table_keys(St, Keys) -> luerl:get_table_keys(Keys, St). get_table_keys_dec(St, Dkeys) -> luerl:get_table_keys_dec(Dkeys, St). set_table_keys(St, Keys, Val) -> luerl:set_table_keys(Keys, Val, St). set_table_keys_dec(St, Dkeys, Dval) -> luerl:set_table_keys_dec(Dkeys, Dval, St). get_table_key(St, Tab, Key) -> luerl:get_table_key(Tab, Key, St). set_table_key(St, Tab, Key, Val) -> luerl:set_table_key(Tab, Key, Val, St). get_stacktrace(St) -> luerl:get_stacktrace(St). ?DOC( """ encode(luastate, term) Equivalent to `:luerl.encode(term, luastate)`. """). ?DOC( #{group => <<"Encode/Decode Date functions">>} ). encode(LuaState, Term) -> luerl:encode(Term, LuaState). ?DOC( #{equiv => luerl:encode_list(Terms, LuaState), group => <<"Encode/Decode Date functions">>} ). encode_list(LuaState, Terms) -> luerl:encode_list(Terms, LuaState). ?DOC( #{equiv => luerl:decode(LuerlTerm, LuaState), group => <<"Encode/Decode Date functions">>} ). decode(LuaState, LuerlTerm) -> luerl:decode(LuerlTerm, LuaState). ?DOC( #{equiv => luerl:decode_list(LuerlTerms, LuaState), group => <<"Encode/Decode Date functions">>} ). decode_list(LuaState, LuerlTerms) -> luerl:decode_list(LuerlTerms, LuaState). externalize(St) -> luerl:externalize(St). internalize(St) -> luerl:internalize(St). ?DOC( """ Puts a private `Value` under `Key` that is not exposed to the runtime. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec put_private(LuaState, Key, Value) -> LuaState when Key :: term(), Value :: term(), LuaState :: luerlstate(). put_private(St, K, V) -> luerl:put_private(K, V, St). ?DOC( """ Get the private value for `Key`. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec get_private(LuaState, Key) -> Value when Key :: term(), Value :: term(), LuaState :: luerlstate(). get_private(St, Key) -> try {ok, maps:get(Key, St#luerl.private)} catch error:{badkey, _} -> error end. ?DOC( """ Delete the private value for `Key`. """). ?DOC( #{group => <<"Private Data functions">>} ). -spec delete_private(LuaState, Key) -> LuaState when Key :: term(), LuaState :: luerlstate(). delete_private(St, K) -> try luerl:delete_private(K, St) catch error:{badkey, _} -> St end. rvirding-luerl-772b25e/src/luerl_comp_peep.erl0000664000232200023220000000723715114130670022042 0ustar debalancedebalance%% Copyright (c) 2013-2019 Robert Virding %% %% 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. %% File : luerl_comp_peep.erl %% Author : Robert Virding %% Purpose : A basic LUA 5.3 compiler for Luerl. %% Does peep-hole optimisation in the compiler. -module(luerl_comp_peep). -include("luerl.hrl"). -include("luerl_comp.hrl"). -include("luerl_instrs.hrl"). ?MODULEDOC(false). -export([chunk/2]). %% chunk(Code, CompInfo) -> {ok,Code}. %% A chunk is now a list of instructions to define the function. chunk(Is0, #cinfo{opts=Opts}=_Ci) -> Is1 = instrs(Is0, nil), %No local state luerl_comp:debug_print(Opts, "cp: ~p\n", [Is1]), {ok,Is1}. %% Combining instructions. instrs([?PUSH_LIT(L),?GET_KEY|Is], St) -> instrs([?GET_LIT_KEY(L)|Is], St); instrs([?PUSH_LIT(L),?SET_KEY|Is], St) -> instrs([?SET_LIT_KEY(L)|Is], St); %% Must check these properly, probably seldom used anyway. %% instrs([?STORE_EVAR(D, I),?PUSH_EVAR(D, I)|Is], St) -> %% instrs([?DUP,?STORE_EVAR(D, I)|Is], St); %% instrs([?STORE_LVAR(D, I),?PUSH_LVAR(D, I)|Is], St) -> %% instrs([?DUP,?STORE_LVAR(D, I)|Is], St); %% instrs([?STORE_GVAR(K),?PUSH_GVAR(K)|Is], St) -> %% instrs([?DUP,?STORE_EVAR(D, I)|Is], St); instrs([?PUSH_LIT(L),?MULTIPLE|Is], St) -> instrs([?PUSH_LAST_LIT(L)|Is], St); instrs([?PUSH_LVAR(D, I),?MULTIPLE|Is], St) -> instrs([?PUSH_LAST_LVAR(D, I)|Is], St); instrs([?PUSH_EVAR(D, I),?MULTIPLE|Is], St) -> instrs([?PUSH_LAST_EVAR(D, I)|Is], St); instrs([?PUSH_GVAR(K),?MULTIPLE|Is], St) -> instrs([?PUSH_LAST_GVAR(K)|Is], St); instrs([?POP,?POP|Is], St) -> instrs([?POP2|Is], St); %% Doing sub instructions. instrs([?PUSH_FDEF(Anno,Lsz,Esz,Pars,Fis0)|Is], St) -> Fis1 = instrs(Fis0, St), [?PUSH_FDEF(Anno,Lsz,Esz,Pars,Fis1)|instrs(Is, St)]; instrs([?BLOCK(Lsz,Esz,Bis0)|Is], St) -> Bis1 = instrs(Bis0, St), [?BLOCK(Lsz,Esz,Bis1)|instrs(Is, St)]; instrs([?REPEAT(Ris0)|Is], St) -> Ris1 = instrs(Ris0, St), [?REPEAT(Ris1)|instrs(Is, St)]; instrs([?WHILE(Eis0, Wis0)|Is], St) -> Eis1 = instrs(Eis0, St), Wis1 = instrs(Wis0, St), [?WHILE(Eis1, Wis1)|instrs(Is, St)]; instrs([?AND_THEN(Tis0)|Is], St) -> Tis1 = instrs(Tis0, St), [?AND_THEN(Tis1)|instrs(Is, St)]; instrs([?OR_ELSE(Fis0)|Is], St) -> Fis1 = instrs(Fis0, St), [?OR_ELSE(Fis1)|instrs(Is, St)]; instrs([?IF_TRUE(Tis0)|Is], St) -> Tis1 = instrs(Tis0, St), [?IF_TRUE(Tis1)|instrs(Is, St)]; instrs([?IF(Tis, [])|Is], St) -> instrs([?IF_TRUE(Tis)|Is], St); instrs([?IF(Tis0, Fis0)|Is], St) -> Tis1 = instrs(Tis0, St), Fis1 = instrs(Fis0, St), [?IF(Tis1, Fis1)|instrs(Is, St)]; instrs([?NFOR(V, Fis0)|Is], St) -> Fis1 = instrs(Fis0, St), [?NFOR(V, Fis1)|instrs(Is, St)]; instrs([?GFOR(Vs, Fis0)|Is], St) -> Fis1 = instrs(Fis0, St), [?GFOR(Vs, Fis1)|instrs(Is, St)]; %% Tail calls for when they are implemented in the VM. %% instrs([?FCALL,?POP], _St) -> [?TAIL_FCALL]; %% instrs([?FCALL,?RETURN(_)|_], _St) -> [?TAIL_FCALL]; %% instrs([?MCALL(M),?POP], _St) -> [?TAIL_MCALL(M)]; %% instrs([?MCALL(M),?RETURN(_)|_], _St) -> [?TAIL_MCALL(M)]; %% Nothing to do. instrs([I|Is], St) -> [I|instrs(Is, St)]; instrs([], _) -> []. rvirding-luerl-772b25e/src/luerl_lib_debug.erl0000664000232200023220000000456615114130670022011 0ustar debalancedebalance%% Copyright (c) 2015-2020 Robert Virding %% %% 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. %% File : luerl_lib_debug.erl %% Author : Robert Virding %% Purpose : The debug library for Luerl. %% This is a very rudimentary debug module which contains those %% functions which need no detailed information about the internals. -module(luerl_lib_debug). -include("luerl.hrl"). ?MODULEDOC(false). %% The basic entry point to set up the function table. -export([install/1,getmetatable/3,getuservalue/3,setmetatable/3,setuservalue/3]). -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this install(St) -> luerl_heap:alloc_table(table(), St). %% table() -> [{FuncName,Function}]. table() -> [{<<"getmetatable">>,#erl_mfa{m=?MODULE,f=getmetatable}}, {<<"getuservalue">>,#erl_mfa{m=?MODULE,f=getuservalue}}, {<<"setmetatable">>,#erl_mfa{m=?MODULE,f=setmetatable}}, {<<"setuservalue">>,#erl_mfa{m=?MODULE,f=setuservalue}} ]. %% getmetatable([Value|_], State) -> {[Table],State}. %% setmetatable([Table,Table|nil|_], State) -> {[Table],State}. %% Can set the metatable of all types here. Return tables for all %% values, for tables and userdata it is the table of the object, %% else the metatable for the type. getmetatable(_, [O|_], St) -> {[luerl_heap:get_metatable(O, St)],St}; getmetatable(_, As, St) -> badarg_error(getmetatable, As, St). setmetatable(_, [T,M|_], St0) -> St1 = luerl_heap:set_metatable(T, M, St0), {[T],St1}; setmetatable(_, As, St) -> badarg_error(setmetatable, As, St). %% getuservalue([User|_], State) -> {[Value],State}. %% setuservalue([User,Value|_], State) -> {[User],State}. %% These are basically no-ops. getuservalue(_, [_|_], St) -> {[nil],St}; getuservalue(_, As, St) -> badarg_error(getuservalue, As, St). setuservalue(_, [U,_|_], St) -> {[U],St}; setuservalue(_, As, St) -> badarg_error(setuservalue, As, St). rvirding-luerl-772b25e/src/ttsets.erl0000664000232200023220000002737215114130670020220 0ustar debalancedebalance%% Copyright (c) 2013 Robert Virding %% %% 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. %% File : ttsets.erl %% Author : Robert Virding %% Purpose : Set as a 2-3 tree. %% This implementation uses 2-3 trees. The description of the tree %% restructuring which is used comes from Prof. Lyn Turbak's notes for %% CS230 Data Structures at Wellesley College. -module(ttsets). -include("luerl.hrl"). ?MODULEDOC(false). %% Standard interface. -export([new/0,is_set/1,size/1,to_list/1,from_list/1]). -export([is_element/2,add_element/2,del_element/2]). -export([union/2,union/1,intersection/2,intersection/1]). -export([is_disjoint/2,subtract/2,is_subset/2]). -export([fold/3,filter/2]). %% Extended interface. -export([foreach/2]). -compile({no_auto_import,[size/1]}). %We mean our own size/1 -ifdef(DEBUG). -export([check_depth/1]). -endif. %% Data structure: %% - {Left,Element,Right} %% - {Left,Element,Middle,Element,Right} %% - empty %% %% The term order is an arithmetic total order, so we should not %% test exact equality for the keys. (If we do, then it becomes %% possible that neither `>', `<', nor `=:=' matches.) Testing '<' %% and '>' first is statistically better than testing for %% equality, and also allows us to skip the test completely in the %% remaining case. -type ttset() :: empty | {ttset(),any(),ttset()} | {ttset(),any(),ttset(),any(),ttset()}. -export_type([ttset/0]). -spec new() -> Set::ttset(). %% Return a new empty set. new() -> empty. %The empty set -spec is_set(Set::ttset()) -> boolean(). %% Return 'true' if Set is a set, else 'false'. is_set(empty) -> true; is_set({A,_,B}) -> is_set(A) andalso is_set(B); is_set({A,_,B,_,C}) -> is_set(A) andalso is_set(B) andalso is_set(C); is_set(_) -> false. -spec size(Set::ttset()) -> non_neg_integer(). %% Return the number of elements in Set. size(empty) -> 0; size({A,_,B}) -> size(A) + size(B) + 1; size({A,_,B,_,C}) -> size(A) + size(B) + size(C) + 2. -spec to_list(Set::ttset()) -> [Element::any()]. %% Return the elements in Set as a list. to_list(D) -> to_list(D, []). to_list(empty, Tail) -> Tail; to_list({A,X,B}, Tail) -> to_list(A, [X|to_list(B, Tail)]); to_list({A,X,B,Y,C}, Tail) -> to_list(A, [X|to_list(B, [Y|to_list(C, Tail)])]). -spec from_list([Element::any()]) -> Dict::ttset(). %% Build a set from the elements in list. from_list(List) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), List). -spec is_element(Element::any(), Set::ttset()) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. is_element(_, empty) -> false; is_element(E, {A,X,_}) when E < X -> is_element(E, A); is_element(E, {_,X,B}) when E > X -> is_element(E, B); is_element(_, {_,_,_}) -> true; is_element(E, {A,X,_,_,_}) when E < X -> is_element(E, A); is_element(E, {_,X,B,Y,C}) when E > X -> if E < Y -> is_element(E, B); %Middle E > Y -> is_element(E, C); %Right true -> true end; is_element(_, {_,_,_,_,_}) -> true. -spec add_element(Element::any(), Set::ttset()) -> Set::ttset(). %% Return Set with Element inserted in it. add_element(E, T) -> %% Store and check for a returned "Up" node. case add_aux(E, T) of {up,Lu,Eu,Ru} -> {Lu,Eu,Ru}; Node -> Node end. add_aux(E, empty) -> {up,empty,E,empty}; %"Up" node add_aux(E, {empty,X,empty}=N) -> %% Special case to avoid creating temporary "up" nodes. %% It helps a little bit, but not much. if E < X -> {empty,E,empty,X,empty}; E > X -> {empty,X,empty,E,empty}; true -> N end; add_aux(E, {A,X,B}=N) -> if E < X -> %Down the left add_up2_l(add_aux(E, A), X, B); E > X -> %Down the right add_up2_r(A, X, add_aux(E, B)); true -> N %Replace current value end; add_aux(E, {A,X,B,Y,C}) when E < X -> add_up3_l(add_aux(E, A), X, B, Y, C); add_aux(E, {A,X,B,Y,C}=N) when E > X -> if E < Y -> %Down the middle add_up3_m(A, X, add_aux(E, B), Y, C); E > Y -> %Down the right add_up3_r(A, X, B, Y, add_aux(E, C)); true -> N end; add_aux(_, {_,_,_,_,_}=N) -> N. %% add_up2_l/r(L, X, R) -> {L,X,M,X,R} | {L,X,R}. add_up2_l({up,Lu,X,Ru}, Y, R) -> {Lu,X,Ru,Y,R}; add_up2_l(L, X, R) -> {L,X,R}. add_up2_r(L, X, {up,Lu,Y,Ru}) -> {L,X,Lu,Y,Ru}; add_up2_r(L, X, R) -> {L,X,R}. %% add_up3_l/m/r(L, X, M, Y, R) -> %% {up,L,X,R} | {L,X,M,Y,R}. add_up3_l({up,Lu,X,Ru}, Y, M, Z, R) -> {up,{Lu,X,Ru},Y,{M,Z,R}}; add_up3_l(L, X, M, Y, R) -> {L,X,M,Y,R}. add_up3_m(L, X, {up,Lu,Y,Ru}, Z, R) -> {up,{L,X,Lu},Y,{Ru,Z,R}}; add_up3_m(L, X, M, Y, R) -> {L,X,M,Y,R}. add_up3_r(L, X, M, Y, {up,Lu,Z,Ru}) -> {up,{L,X,M},Y,{Lu,Z,Ru}}; add_up3_r(L, X, M, Y, R) -> {L,X,M,Y,R}. -spec del_element(Element::any(), Set::ttset()) -> Set::ttset(). %% Return Set but with Element removed. del_element(E, T) -> case del_aux(E, T) of {up,T1} -> T1; T1 -> T1 end. del_aux(_, empty) -> empty; %No element del_aux(E, {empty,X,empty}=N) -> if E < X; E > X -> N; %No element true -> {up,empty} end; del_aux(E, {A,X,B}) -> if E < X -> %Down the left del_up2_l(del_aux(E, A), X, B); E > X -> %Down the right del_up2_r(A, X, del_aux(E, B)); true -> {Bm,B1}= del_min(B), del_up2_r(A, Bm, B1) end; del_aux(E, {empty,X,empty,Y,empty}=N) -> if E < X -> N; %No element E > X -> if E < Y -> N; %No element E > Y -> N; true -> {empty,X,empty} end; true -> {empty,Y,empty} end; del_aux(E, {A,X,B,Y,C}) when E < X -> del_up3_l(del_aux(E, A), X, B, Y, C); del_aux(E, {A,X,B,Y,C}) when E > X -> if E < Y -> del_up3_m(A, X, del_aux(E, B), Y, C); E > Y -> del_up3_r(A, X, B, Y, del_aux(E, C)); true -> {Cm,C1} = del_min(C), del_up3_r(A, X, B, Cm, C1) end; del_aux(_, {A,_,B,Y,C}) -> {Bm,B1} = del_min(B), del_up3_m(A, Bm, B1, Y, C). del_min(T) -> %%io:format("em: ~p\n-> ~p\n", [T,T1]), del_min1(T). del_min1({empty,X,empty}) -> {X,{up,empty}}; del_min1({A,X,B}) -> {Min,A1} = del_min1(A), {Min,del_up2_l(A1, X, B)}; del_min1({empty,X,empty,Y,empty}) -> {X,{empty,Y,empty}}; del_min1({A,X,B,Y,C}) -> {Min,A1} = del_min1(A), {Min,del_up3_l(A1, X, B, Y, C)}. %% del_up2_l/r(L, X, R) -> Node | {up,Node}. %% We use the same naming of nodes and keys as in the text. It makes %% checking the rules easier. del_up2_l({up,L}, X, {M,Y,R}) -> %1.1 {up,{L,X,M,Y,R}}; del_up2_l({up,A}, X, {B,Y,C,Z,D}) -> %2.1 {{A,X,B},Y,{C,Z,D}}; del_up2_l(L, X, R) -> {L,X,R}. del_up2_r({L,X,M}, Y, {up,R}) -> %1.2 {up,{L,X,M,Y,R}}; del_up2_r({A,X,B,Y,C}, Z, {up,D}) -> %2.2 {{A,X,B},Y,{C,Z,D}}; del_up2_r(L, X, R) -> {L,X,R}. %% del_up2_r(L, X, {up,R}) -> del_up2_r1(L, X, R); %% del_up2_r(L, X, R) -> {L,K,V,R}. %% del_up2_r1({L,X,M}, Y, R) -> %1.2 %% {up,{L,X,M,Y,R}}; %% del_up2_r1({A,X,B,Y,C}, Z, D) -> %2.2 %% {{A,X,B},Y,{C,Z,D}}. %% del_up3_l/m/r(L, X, M, Y, R) -> Node | {up,Node}. %% We use the same naming of nodes and keys as in the text. It makes %% checking the rules easier. N.B. there are alternate valid choices %% for the middle case! del_up3_l({up,A}, X, {B,Y,C}, Z, D) -> %3a.1 {{A,X,B,Y,C},Z,D}; del_up3_l({up,A}, W, {B,X,C,Y,D}, Z, E) -> %4a.1 {{A,W,B},X,{C,Y,D},Z,E}; del_up3_l(A, X, B, Y, C) -> {A,X,B,Y,C}. del_up3_m({A,X,B}, Y, {up,C}, Z, D) -> %3a.2 {{A,X,B,Y,C},Z,D}; del_up3_m(A, X, {up,B}, Y, {C,Z,D}) -> %3b.1 {A,X,{B,Y,C,Z,D}}; del_up3_m({A,W,B,X,C}, Y, {up,D}, Z, E) -> %4a.2 {{A,W,B},X,{C,Y,D},Z,E}; del_up3_m(A, W, {up,B}, X, {C,Y,D,Z,E}) -> %4b.1 {A,W,{B,X,C},Y,{D,Z,E}}; del_up3_m(A, X, B, Y, C) -> {A,X,B,Y,C}. del_up3_r(A, X, {B,Y,C}, Z, {up,D}) -> %3b.2 {A,X,{B,Y,C,Z,D}}; del_up3_r(A, W, {B,X,C,Y,D}, Z, {up,E}) -> %4b.2 {A,W,{B,X,C},Y,{D,Z,E}}; del_up3_r(A, X, B, Y, C) -> {A,X,B,Y,C}. -spec union(Set1::ttset(), Set2::ttset()) -> Set::ttset(). %% Return the union of Set1 and Set2. union(S1, S2) -> fold(fun (E, S) -> add_element(E, S) end, S2, S1). -spec union(Sets::[ttset()]) -> Set::ttset(). %% Return the union of the list of sets. union([S1,S2|Ss]) -> %% Do our own unions here to try and fold over smaller set. U0 = union(Ss), U1 = fold(fun (E, S) -> add_element(E, S) end, U0, S2), fold(fun (E, S) -> add_element(E, S) end, U1, S1); union([S]) -> S; union([]) -> empty. -spec intersection(Set1::ttset(), Set2::ttset()) -> Set::ttset(). %% Return the intersection of Set1 and Set2. intersection(S1, S2) -> filter(fun (E) -> is_element(E, S1) end, S2). -spec intersection(Sets::[ttset()]) -> Set::ttset(). %% Return the intersection of the list of sets. intersection([S]) -> S; intersection([S|Ss]) -> lists:foldl(fun (S1, I) -> intersection(S1, I) end, S, Ss). -spec is_disjoint(Set1::ttset(), Set2::ttset()) -> boolean(). %% Check whether Set1 and Set2 are disjoint. is_disjoint(S1, S2) -> fold(fun (E, Dis) -> Dis andalso (not is_element(E, S2)) end, true, S1). -spec subtract(Set1::ttset(), Set2::ttset()) -> Set::ttset(). %% Return all and only the elements in Set1 which are not elements of Set2. subtract(S1, S2) -> filter(fun (E) -> not is_element(E, S2) end, S1). -spec is_subset(Set1::ttset(), Set2::ttset()) -> boolean(). %% Return 'true' when every element of Set1 is also an element of %% Set2, else 'false'. is_subset(S1, S2) -> fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). -spec fold(Fun::fun(), Acc::any(), Set::ttset()) -> any(). %% Apply Fun to each element in Set. Do it left to right, even if %% this is not specified. fold(_, Acc, empty) -> Acc; fold(F, Acc0, {A,X,B}) -> Acc1 = F(X, fold(F, Acc0, A)), fold(F, Acc1, B); fold(F, Acc0, {A,X,B,Y,C}) -> Acc1 = F(X, fold(F, Acc0, A)), Acc2 = F(Y, fold(F, Acc1, B)), fold(F, Acc2, C). -spec filter(Fun::fun(), Set::ttset()) -> Set::ttset(). %% Apply Fun to each element in Dict. Do it left to right, even if %% this is not specified. filter(F, S) -> filter(F, S, new()). filter(_, empty, New) -> New; filter(F, {A,X,B}, New0) -> New1 = filter(F, A, New0), New2 = case F(X) of true -> add_element(X, New1); false -> New1 end, filter(F, B, New2); filter(F, {A,X,B,Y,C}, New0) -> New1 = filter(F, A, New0), New2 = case F(X) of true -> add_element(X, New1); false -> New1 end, New3 = filter(F, B, New2), New4 = case F(Y) of true -> add_element(Y, New3); false -> New3 end, filter(F, C, New4). %% Extended interface. -spec foreach(Fun::fun(), Set::ttset()) -> ok. %% Apply Fun to each element in Set. Do it left to right, even if %% this is not specified. foreach(_, empty) -> ok; foreach(F, {A,X,B}) -> foreach(F, A), F(X), foreach(F, B); foreach(F, {A,X,B,Y,C}) -> foreach(F, A), F(X), foreach(F, B), F(Y), foreach(F, C). -ifdef(DEBUG). %% Check the depth of all the leaves, should all be the same. check_depth(T) -> check_depth(T, 1, orddict:new()). check_depth(empty, D, Dd) -> orddict:update_counter(D, 1, Dd); check_depth({L,_,R}, D, Dd0) -> Dd1 = orddict:update_counter(two, 1, Dd0), Dd2 = check_depth(L, D+1, Dd1), check_depth(R, D+1, Dd2); check_depth({L,_,M,_,R}, D, Dd0) -> Dd1 = orddict:update_counter(three, 1, Dd0), Dd2 = check_depth(L, D+1, Dd1), Dd3 = check_depth(M, D+1, Dd2), check_depth(R, D+1, Dd3). -endif. rvirding-luerl-772b25e/priv/0000775000232200023220000000000015114130670016344 5ustar debalancedebalancervirding-luerl-772b25e/priv/images/0000775000232200023220000000000015114130670017611 5ustar debalancedebalancervirding-luerl-772b25e/priv/images/logo.png0000664000232200023220000005462015114130670021266 0ustar debalancedebalancePNG  IHDRZ=sRGBeXIfMM*V^(2fizHH2024:01:19 08:49:32Uج pHYs  iTXtXML:com.adobe.xmp 1 250 250 250 2 250 1 72 72 LuErl-Logo-14 2024-01-19T08:49:32-06:00 2024-01-19T08:49:32-06:00 Affinity Designer 1.10.8 produced 2024-01-19T08:49:32-06:00 sRGB IEC61966-2.1 3 j'=@IDATx]|{ElY͖{\5hb$@HB $q{muV~|iwnwf~?vy8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#p Z wY8 0A "EA0ZE1EAT[Ec P8 `̖yR N]ↅ &aiK?=C$t4-ֈC@5G3v zIdpAhb]2OQ3[HT8bx,V, Mzl|$# bMQ֊-Ŀ.V: fkD&Mgcub;'N]ُ'r?YXKw5MbLBYq[8" Oh}Su(,AT sܪ0sā~x{h dgO6 #"p4߻h46,ߋD' d<>H-waw"}Є*5gEVc&7ΰkh@Ai->ziwVQ&NLwRVN5bB`̠X Q ɽ;zC}9uDK˪Εu}Yu#7@޹:ί:NkE܍ole|+;&t6M(_Бε|1FH7@(Akvޛ-V8Wep*8ZUoKQ#,)ذ6&gfA5-ՆSæӰD^o5s`~4mIpBz$I?Zjhy4,r̚4WЃJ9~6 Tֶx;"Q~[1c~!'tL3+w0zKY STgjE_e/ם'JeF7oG;~&d<ŨFyAM_WOM.SǦVJw|ܟ] أo¾<]MEΞN\,+AˆÓ<)*DӰ=]ou^ns-Z_3Z6U>P-Dnp{WϧҼ`\{?mhGaS&[O#t!ebYOPHƙ!oTr Է}|xp3m;3~~UqdzL1~A7\ݢ&'GL*!0 vūrjAVo{ʞ{ ~3n_`+y]Iĭc0%Ex?#@J6^}Ʌ^V|NpU€bmo4*,ٺ=\0RֻWLLv +3 ǡÝ__ }C` O޷ZEKʺ+n4.gHSg, Ia+ڌq3 )A7AI< T6û+O[1 (~*jp%>BwnK)3^!Jڪ _ (Ą[:!z=Tj-G 2mVZ uKBOXtnv,~j̙20[k-{ }Q~g W*xӂڻP߬:1:59َ _g8yy#b;_P*eh4†زY vЭȟBëJ;MHT] ^γi!BTedvnRnGx񆢬z_}Dsv><~hXtDВ.Ev8p,ŃPW!<,NuݴfQWnXWg>HYTx^Ig' fK!"*C*dz cb:+O1kwIQ&c^>L[*a4h'n/Ls]]p!^3f򏟛É m_p7nZDNZ4L @[?Dc]H4Xc)j_&i0o 8~?ʫOCJoL %տ6w&vJ /S̊oto .OdBzݓ+zcuHj0[T_HX0mOKXt n5HGgpe.z>(tǣ FZiPT' R& ]G>gQU\#3FGf=>TRf5: gtaP[g:0 "Дj̬R8KNMLEJ| \d@0*IÔ0EVƵ{ޚ:402Y&Aaju|埃aI!uG: x9$fei>@>bv$ü "8y.q?9ߔ5;(kr|Y^FČѾ~e>ss [5[L8t4&YC!Ԉ׋Ņ:'x!H pDN߽9wEDN : Q6ѾK&CzuB8h$HOpX-^n2v "Dx-xu 3Y-*p-W(( OxeVrb<:qx&?phϫc!pyn52V}fX`@\% e?<[$ ^rp[UC]5aʍ.ջЫ+nA$$˴gSU[U=FRu ԕ:Q33OfVsjEk(ѠrUMOBG1I|mSD޺,=hCY=1kٜlz _N;ww kFy^< ; mVv;(ߔ4A7Sg{ұ dn~Ft)Q#/﯁c j!!ZnFj ~dr/knڮ>8n; n8ѐ}>17ͬ. >K~%4>Cmwl9Pv{玾CPfO_ a\~%t(,ph0VXFimegj4#hnnQYLs?#/${m5ǡ[ HaF]ؼrFi`eS7$ST:]ep6;څ'NHihCǍMom2(} 5@t8't%ٌ[BQ01> Pkg ~Pd?t7`ēnyUdhY#6V1!<t!d£uQ0~T_i0vJlie#k 1$m'tAYL|~< SBnMwU' *&3Q#q {AEYE*?S9bRʬlHksW8i]nEIBvJL-Mm[s8_t:4(B8tv8[og06/:WeUdG]*ݏQ{(1}|pH0^iA6@衩u Mx Y3D-~Qq bc!4ɇ n|jࢠ]erE. 1F֐\Y 2`C}۴pLPMT{J'v\7s,}jԢr o}5ۺ R׮Bո$Axdu:ʲ}VqH+?0o=W~t* zNCA Q=㐴XePouf=}8L)kzaΗ8}$E:Z `UnһW;o7RB"|SePtpYR'v[&°ʵ\[ M g ."1>G QH: Y{Cr|D0{j[LkV`zMge;"fGmKe5q]McAԱ-\tE`VC rn:ι;ޅhk^XJU`z|aԁ-kq'%D}$nj%Qajũ@~ p7?`4u k|Sonjbh%ߌ.s}Ժ7pT)[o"PVg"F 4+dOE4cd8DFRoW9qCㇷC:RgԂʾcu׽^6eцҬ]=6k[k 4 xtm3CX p W=d=4ـzkq'@*J(Rj!ܛͺo?T ڻ9Yu'^44ݖIAݒO]s8 učTdfWFʯZTTtTZI>-n={G,82Ĺn\ja0{bZ[v "X5>6BE $(wq<" H\<2 QE;BNljbIM@2I= >,}ʫ;*?˚1x☔L拞hFz8R=q:kQU&J9e_:Y;eR%F͢Cv[Π֫NwV@{{cdFC֘ŶkFu"Йae*&gMO]׳HvcgrO4>ܹ-wg82]O];6jjHԡW*;hWcul!z b) !'ktya\=cGPunqEt$]+[ Lsh D"ngj[_&Js2α~AokQα^KvG+G.ϺG{)3Js_r _o8ԉ^ᐚ9Ew&*2SA&(2lB}Q{3:RʜrIm<~w zu\-#!D>"t >vCo<9 PӤu{`2! wRPR-jıމ#v+Ln3ġjp|j :f$(rȴXwKԞك+ڽkт[hJNrx;Q vɜ kD5ųF$pU mLV_?Co3ȶ3'b;7ˬ[s \8M(&0 -B3y Y 8#?M ԑOHI≻ܝ_t=z&nyG8;:IU:׶#h|95}jlY0-ۺg;xiKI6=*kiK2<ϛ֌`zN0C/ R"&\} kRRlhl$ߕ&f{jlRpWZoBxd/xsL裯sxhHǖ4כk5 ZQuvrkhlZ"Bj}s=t;On Rr s:S޻r$t8xЦF]DL--xTuM}Dx ԤYC2He:x Y?L``ڝ]ƌ3cmͻuǯa6SB{ɩrCG=LJH?9a$XR;ьk8xYG3x~Z-Z،QtaM6<\ "<'$\5HM4wr全oowγmftV.ʳ4H= R^{~ԌS3cK= ,] gh E&KFn=DqpKόgK@ǒxai#W!z#j=zm*l<>Kl` ]jeC%= r/9Π0';#N̈́aS/gd[?>uit*YXnC#`keD֟@Zk}TEM :+PUG)ѤQL3qú{3I맇Y3ZfrU!:[ Pnhv]rqoEbX64'Y-M6+1!FBpvF {0_g%؀>\4<E7ej]{(U֬;˯g y0fTZ3]rC b݉0b f-L?-a*4%-#:gۅpikl^1 +6m77RStۍA䳂|xdaO2Ai{^5ْ[ XEQZi`͘@A ?R+r YpGv}ۮl/VV,N6x! GCD{ %EE6t:{5AR\ckr\V^ctЌdЅAZsqàe@r- S-]Nbk=˨ 6RD-Z#\zVo93g:}j[tФ1Y H4в;&e/8_O $ oK_dSO a/'=(﨔$& bDp: ^퍊PYQ-Չ=i`rӘt"ft:O@hnIe 'qO/Dڋ^I@kF%%}L~͐[p׻pKo (Tt#keh"D$tc2)"~:kkEVbA(T?[)_IB?|OWrDY3ZxSC$(\'Mͧ623Rw6-1wIoG[Eljb)m\x`)mMC2V:u\R[b jY[н"\X.}?@g-57MUJ?`,u'U3X8PGkt[%C liOKNu?yY/Z"֌NfFPg o(K`y:>II 0c|k$ܱʀZs16ض=(Lx/.֖fjIK W{]8T{oY_J PJCxX(*6/3P9g"e .)먳MrEҌ.G]kt"h<mu &LA .kw6uQw.fMjzdmU8\ U6T@=W4=O[a Gw.INAX\\=;eF{bR2|0$-nJ=Z;RųL:c V^JnjtvX%c Q2TyVNwF0+[ס8C[ Z{ iClfKǻpuMOlء.\76\9n|Њr&~ oPwV| sF''9Ł?ft6ftdgt:`0I1I6KdϜb4Fu#[T}m"P)qmƒje'֣ehn2P*m--jZB`'}ɭklHcRYuTc >B앇lrk슶k%r/9 Rw9 3:r\Gƕ5\ |Ǖ -$ҟ?5\p?KD@ AB-?zb/0c?ܱ-&79 dw-|jii\_[M?oFim{͐G&. IZD=< Gօ&;6:F1KpUu-O'xv>rnp,C6Qև3:~pEqP;AmvUi];ևHy^Rz!.LYYӌ]bءŢR;dzὭ%69?n=-yC(wމBaĀ-7dž AeSǹpٌQpeCu:Ϟ㺼}iºf珣k9&85;bZ,7zgVr/9 Bg}pY֘$PǛ\/) dwIfՠHl^8qV=TZoNwm<& NSN#UA#p9aHoXbKZ\mB:~$w*(<(,nPHy*T̷KRf.8H9 -{C[1923wnrn㧓SH؂/ Ѱ$BWmr v J˫NF4ZIBh9,CwPM8HN]F|E<.* /뗍U87Z/<#?"NGbs~&,CQP<`|ыn%Ѐ2A<R^$tX%_cU4/dچz}]p}Z,"b^)esCPVnO#V{XXcqqw657M]=n|j%D;KOcj#KRsrϜ|(=jۭmR4orjQHYTnslrDD_tf%>L$=NC)5Y[/Mtq0@*66 кĨ0|P {VZgZIvұϽpk4/h,m*`G4Α ً@>E[|Ѷk~a=N̅!v~/~WJkktKlTJkɇq'CC,wܮ3K$xdR\d#lؓ?>@Q/>2 SGgy#tx;_8({#Aˈ&xsI#q?v. ,^"Pf9|4*/3:YoKaR`z|lo;NW\~Q aӕ2xaܘAPks e\}xig~p^ |6x폳T]b359plu6tUc/FVLqyn%J<VEDymgI$)"IM־ټk4@mhRN?.; =b=VL  - ЈguOKy5^өQahdT /FF{AцmΥ|wOYG|9&NLfu7D}G%I:-RR:Zʪ:xUzW37䐃>?C\:CM}3T##CQ? e$'DwV@ZfʦZft[# J2|TT`Re:kՖ3gtAF2b(|{U#ы=xh,fK g KnWߎeRe9twb{PBol;aWB/\Q3:Ha(LfKh8"t(TOiQB},:: Lp4!}o[OV%lm[\F{Vr 6,ʠvP5Opd\sMhS5E}rgCiw,$ԡ?)tY$/v8K\t%)(hOX߷}g!E煑m6n4'mVy:NJ}s !!i,M*Q11ݩӬ5:35{\_:u3o9F]wÌ1p$?=uaG~H㌡=@;}Xj#tLnxl-ռ'琋^[EhcΜl6 2ae'eUFͦY5+7[&+ ֌.WJW ͆Z-6Fdπ;(QEk3ŵhK.O{D~iB'9;kg߁Sm&]ٌyU@D!SA)~xn_ǖquVsNUyNYX3SF(G'_^7RQ/piOEg ɣ%D1)]^Ԙ31JkZDڡcJk)ąL?,]cۦ=QQpjD+̺3FXzNvB4˧ .GicWÁRh\r]*O)/3ӟK*?Ѝ艡Ja! e)KҖ~gc@ Σ`>LfK ֝:K]? t?Wֶ H72rˊ8GLMᄚ%: >SW\:BG-FIh@Ni] %MscQo1p*=#1XG~(Nji# {Nu魿'o,mu0k6Un82Rcqv0dC}Ӿm}N3:u=ߔU+7 jq8ml\ޭq_1Y Zʒ5QpFAEvrw8;e4lw1׼ o   s %zv_guoބPtԝNySXi]x![QYVIYP Mn(~O7&sb =wV'`Max=S4|Dx[h0h6ggMd}Hw8e( iN~3?Xu ]FBܭq_Ur;s#h~&d3l:oB9-D}ɚ384 Ѱ[Se>/Ȋ}Eϲ۟pP:u&wc 6qci>Vxjj8Sa;\An% ]B߭U-k F{&` :B1A+ҖӑŐ#&ޛRMP_=@ O~d{qm}fgIܢ-6|qV/oxj<0)&F 0,iU1CH7^Gʚ}JҪfg-s*X ܝUx++?/u<K䪲gM 𭧻((TYBpMupZQ0k^769h E\YdIkACB9"LƒN;ai}qˍ @erݝsh%h]*ظ`~ ܝ%> CXcP M"y lOMV[fzخ㛢7&uz >[`AOjsD L3t NpɰH((@0-H~̝_/(i`hsmL^Rq aRd XBK 냏CCP 1?DG犻 n5;NDt 6YaH3)B.NAO. / 9ƒiŇPe=;RwM8G2K0WN>_w0|@Ȼ<^47|iem笻)ɆhX疻sxUq?*wI*pf?+} '+*Xj@!xN;zX; ơYG&磣[@rԬ*c.Ȯ(weꖄNϠ:gGZ3xԠ$[;.bb b2:>Z} !V)]*\'5h@&?kY@/.6.[E8WDMT#4\ Iliqj=MO3iOglsz-S$i^7)*CY;[&j40?/kYf[L7քNϫ.og1 Z`F{VmɵmHuz<A0ԥ$Uodܫ]x0Gu^A抠xSu{B' P7ތVo_7zR%B 9 ixPĠ8M'ZLT\1?|>f킭7l;gKdz)-|,=VTjw ~ˏ&g,%*։NiOJ=WZ+j۟]* ᐋ|ϯoɞ(\Fn~Oļ=bFwkvGO\k sL>]X x} On$Ϧn)ںD]n#5pJZz] ╅˿BqNO( ::}'LpYhݦ3i}{}whk!sd-Y?\h9Jlm"l/+޸d;HBTz ]I=`~db=5:feYز? |2i]1j[W(]N:96U֦( /)n?<**ŚwӁ19I2ȁQ荣 cb:$pTJN 0K( Uh_Aȃa!03dOf{.,l/vz|FoXC rBA'Xx n-"^<"n5.WX< KlĽq_>d)c;Y /a٫V4]YN#pb(0np,ܐ1FtUp;%`Vnh;/mdϞw-t$N>ٛnҦ&*ZD V`5܉)S=z!,p0 e‘e@ ,b8{l/h *c℮£N`DX1 a1Y~6ۤH"7DDGϔۈ2wJxQ>_8" [$tFrdׯ8J68A !m‹9!୴9kAƘ|݃;l]kRf-$X[p"V0_y0{pBWk2*/Z {#WRTO]/u o\y8wRf,pTٟ¡8P`㛇/UpB*} i(NAUݶc4M2D61fFߕ;lU9ҧucщ@U3g䋃=B,qCAw(sIJmj|'tji+OiU;Њ<;?H"X HksEd/k.VנᯈP\D5~PZtB\ #p8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#p8G#!xe/EIENDB`rvirding-luerl-772b25e/priv/images/logo-large.png0000664000232200023220000165246115114130670022366 0ustar debalancedebalancePNG  IHDR qysRGBeXIfMM*V^(2fizHH2024:01:19 08:41:23  pHYs  iTXtXML:com.adobe.xmp 1 3000 3000 3000 2 3000 1 72 72 LuErl-Logo-14 2024-01-19T08:41:23-06:00 2024-01-19T08:41:23-06:00 Affinity Designer 1.10.6 produced 2024-01-19T08:41:23-06:00 sRGB IEC61966-2.1 3 @IDATx[p]Wyl Ƕ (Nm.Zg0StgfNxcgĤ&5j u"XW}2dı,Y:>g_^o9q朿R @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @@BI @ @=%J +;V=4ت`j}IulPsK8^\SξK.u{rb#W͹86NR-VZl>/Cv0^Wg&Ro#@ @ @!   @ @lۇSѮZe.Un#ånG{Gc)Ǿ*xwD^s.-`u豷&^_,usZBn%Pr% oFF @ @* ޫ3. @ @] 1~{HFr]TFb=jGP7NP;Ī3a&߄s'/rN @ @ @Kܻd  @ @Z#+y`io6Z)u*GRSU T]*-9Ty6TO\Nm @ @ Q% @ @+[yoKիzF` w W us.(f|Ou-U50ّX9~F @ @k, Ơ#@ @ @V\V>XfxO4ԫ; &? -)V GK=[N9G @ @o˛=#@ @ @&;/߶GKTGˣA"Q,k)]jB*9_t$ޟKWf~m\#"@ @ @ܻz  @ @w^ՕF`L^?S @`LNLSUjql07 @ @}# 7SP @ @='~ n-UXz=W3r,>kG>? ·R̖̯y( @ @ ;3*!@ @ ЋO|ź}U:CUIcQh;{h5 @:g+G ~R=c6+ @ @ 5]3UJ @ @g:+ P&aC%&.޳S0nK_S)r.ӹӯ.mWo= @ @% ~]\.&@ @ @`Ň{`m5ܬU6XTtB.yܮ/\]~jjwN @ @^@}v$@ @ @oWug5X]V @`3 cp{PRK{B^^~㰍 @ @# >z%@ @ ++Uk,Xy,`XJ=" @ @ܨ  @ @@ }ҁ*w\rKiQp2Xsqtr: ןcV}_% @ @{͸z  @ @U >~UR]"81X*η^r.ȸbϗg=Z @ @C@?Y @ @iZ=C? @`M"S|j+/ux.  @ @dlB  @ @u O 쯯|֑\cU=Տ  @Xȩ< wz`6=t#@ @ @zܯG˵ @ @6PnmwRُRp>mˣ  @F "SNK~:|w9(&@ @ @`s' @ @uѿ۹}puTUk>m`c uhSTU婫i;Wz׃U)foVeGbD:*'@)t!~l _moyʣ_OU @ @c @ @/n>ڬ)#+UE @gtjUjz B @ @` o 2< @ @˻:+,F~Kw(  @kD=OWyjhC/_  @ @X*B @ ޝR}Jx)Dq(rSUnV{?g=pK @ @`ZT @ @='p'w.K;O\ه{P @7"78t6\;~H%@ @ Џ8j&@ @x[=/}:Ve"ӝo{ @ΕϦ\۶-=\|˝!@ @ @@ݟ @ @}Є؏#ԞEFXk&>Ur9su>߸ @ @np3v @ @c[cvx:  @%tUK9Vym~'SS-o`< @ @F o x> @ @ ;7Zyc%uVfompc @ \.~rU˹ťsϟ7ޭ @ @t{w̓Q @ @\[ uKuON垸k  @U0Vy/g:w1DX6@ @ @p_[ @ @6@RI%Z'Оl @`}^ϖ&S&G^yGSS-#N @ @gO!@ @qXVhNFn[= @^?R,Lza~J @ @}) ޗӮh @ @@7LTO^KU+%Vhh/uȍ WKGL[{~(3 @ @Z@]S @ @`]>־G>tO. U7SJ  @=+JA#<;T:}ݳ* @ @`C7  @ @-pSmY:K}h?̩  @x1R:~\  @ @[_6[) @ @4ywS}*b ѶG @@/rNgR,og>P6 @ @. @ @,o+{ZiR'&ԾU @ ,D~?LZ_)M7ԣ  @ @J@[ @ @k }[NR8@h>G6+ @"/\?,`^7{% @ @_L @ @>ȣ'→ScoѶ  @W৩3uUJߟ>N @ @& m3f @ @57=|o _nuA @zR)-zUUg~}㚻|r[Q9 @ @K c @ @Ȧz+y]ک=_  @tеK{tE_  @)E<ˮ/dq>e^ @ Pκ$@ @*\崋7dQ8;ΐ oI @B`MR=]qII @ w}gkd @ @,*.< oVgYvC1+^Ѫ/)F @ PeJ @ @jKeE oKFS[ @@ L0bvÓ\TU  @ @@ bk @8\<{kȁ @@ُyGknlK @ @@!  @ @I k]p  @'uu׮iݻY @ @p= @ "y :ai工ߜ{) @ @OEnu6X8 @ P+$@ @)P oBoK7y @80 ;vt]Wp&@ @T{un @ @n ,*ޕ9鲱ݾԉ @i%_[qiĢb k @ @@ Wک @]˧.!@ @@_<k.;M$$@ @{ @ @hZ Y;@Jx@ @ @oʲkQXŏTF'@ @{yj @ @^z҇-[4Az0 @OlW7g_&#@ @ vݔ @ @@L_CcS{4ˠɨ @wxG꡻\vm^  @ @ Ȧ @ @Ps7NYxWziQ}8  @ P?z߽EmP @ @!  @ @[ k:£Bjg|J`> @^qiĢbԥ  @ @~  @ @zE3]is @ @VG _[{Ryת4 @ @^E @ @\&pBC=jN(A @rH Y~uQ[/ykU @ @/ @( Ig~lxî7EW=^+A @Idv}ݏX_- @ZYi} @ @ZN]8?+ޚ^% @%Й~ Y_:4_Ż+z @ @5! ^ˬI @ P-]Ԝw,*go*#@ @@U <}-ϲ/^rUՙf @ @pS< @Ly :qQ (Tf'&@ @@U_;^}gSݍ @ 0pgof @ Ps ?,,wj~|h @-Ж>@V/q5] @ @jpU @(#g|peSYǖQiJ!@ @} <;>!@ @{z @XThYe~R>  @T@g_9oم  @ @JpR' @ [ {S3+d% @ @@Ͳ&ٗV.tYʽ @ @zW@w=F @jN`k޹3N;vk?3Sݍ @\,˯,6|e^Yj @G~6 @&" ޝWM @-P_m\]ݾ҉ @ @K @ @/3|gD* @Ԩvrw%Rݍ @ #q9 @ԢB B $0L @=/K⿯e]d @ @kz5O @-0}'wFbevu' @ @@I@ @ Bԅ<>Yi @}YW4 ʭ0K  @ @*p @=8<ku#@ @!lb5\% @ @@ Wj @@eRҹ_|  @ @@/ ܙ;v5w]Y  @ @5. ^'@ @څw(5).FmbA]G kacؐ2.cpC] z^kKB JcsKItfx=칄/Yz腯^sKGvǿ~}k]sy5{fnm?ޕ}ϿŮJ>kXz/|-]jgwؙvK|7 P&yܜQ~IU @ @I๷i6 @ @,rפ=h?&~}{/Wt&F#bء1qH98FPu'>DOA)FO B3kU)/ Nm;~}fwl}x3m]Ϸ?;?ӞڣƲ+}:  P ?,_O.JX @ @ P @ @NiGfഋzA}ؠvT6tP O_w}M X&.kbJ殺{)>_Z wRX) _-*N {YWq5W  @ @pka @ @Ț,<LJޑSBG{C1K;N=!Æ ]@GG ŧ{i] oIuvƆMƆ-;=]F$"(._SK @Ԋ{>  @ @j& mm<> @ @@ I @ @@/ ,XTR|ه#{iT @ TSh}fˈh8"&SX=G#Fy[]NO::as;v/Ku#@؝Q/[}{ʪ2 @ @IJH @N`ҫϝpn qi*13jOP#੣z)>6^ Iƴ c5.iۮXok}_n{ ƿAO KBvO\9Ř @聀{J @[`+ENsGZ=bP1{\217IcŸ{v]o54؇F][G-(ݟy-VV{&v)G-x勇/-c> @ @|*Sn+ @ io=3bƙ@ @겘42^11f6#b1alcF; \MذygYsd @^Ya%,q/ @ @e" ^&  @ rECcpn%C @@J!Y)>kLJ!b/^ O7".n$bLK]x3#%U+#̯O^z7mZ>k @ %  @ /N{rLOhhbn8lf)>k'i 1r]amgm }[W/W{&vu#@@ dLˢp/Y j @T{E.  @ @Z<+\~8WgP5@)~8lƘ<*Z&ǥ]ػ#bP]  ?zkh\::Z&I)>aLڍ}܈2j{Z`X+7w}}$W; @:"첕7_vgmS @ ٛ @jHˆ,.Lm\Ck>yDiGq1)>iT4M%^'@vuƲU)޵K'+XE{]:_dQGOg @ @y%|%@ @} 0 t?qn~\LaH!qYccfsڕ}h8:G -A'QJ]IkE_ _]mu_\u%_ @ @p5P @T@ ,ޙkD@ <>=.fvd4nd no m P]ݟH!GW{ ?䞯m5(ej@׿Zg\z5Я  @ @ iML @'м`<_S? PKÇżqq1e͓FADW@1ϻvwߗ:z0S  Py(dZ2:2^* @ @@e W溩 @H`ҙ>h/Le-ҔB;S'MC7#i¨h<:G}@#rbx$^6E)FJH{=Xcv*5 @ @rp/UQ @T@i5gIh SE+h+ıcs[ufƏtw#@,cW{qo 說OE~^  p`=@ @z(0ԋ.{gr @_7S_G[ybڙ}P/:ݟ?J}=]G@ l"\}}3Oxٺ~d @ @ܫh1B @}+rEggśv& @g ӎvgM3c⸑= @ޟKbW4jX`w IWQUK.YVZ'@ @% _l."@ @Ț,<,"?*e*0rh}x81wژ쭓P+NB.u-B}mTO>.+ħVoW @ @p51 @tK`QMS=%N"@@/ \2.'Ɯ)Ԙcc萆^p @:J^z/H)aXa]`wn>7</x<1oj| @ @qY @ @@m <ャwpم7Om  W Yw8cczScLMҸ @@J{w>.muytT [gbV= @ @ܫqUD @/+0 &FN'z @lgGcZ1lpFp* PܒkS}]4}]n{y9TP˕嗬^xU% @ mnS9 @A`iVW!ГXQf?~ޤ;mLHcF lQf'@D`]A>)::}2A Y<^qjVA @ P5'@ @@L=yy!4}H\@aZ'3QW6[o;T:O;]X?h}^ H?e\Y(~jMV* @Oz@ @ P\pl~S/0mxmW}bڡ}O ^ B 0<xx_{B6=;jb!>zeN}ԛ^ @ @5 @tyyfQJoFqq-qAӚƆ@{ @@5 XkwRʭQSiB~e[o9Eҏ"@ @p_o @ @@ ,*.hY @lw>.~]ytcwL$@BU7E*Sk @ P>х6 @J`{`U՛fURnic[∹cδ1uJ)ޫ mRG7OOR=K!x7tC !>zkݻ @UR) @~K ۛޕ^ @Jf6ٳC{d@\w>6}]?S+x &Ư0= @ @[sZ643i9=(_BJu11N?%^1{\M;Ln,߂UFkƭwX?wMl֍B@ <+xUaƒE5ؿ  @ @Jܫd!A @HߞG7߃jgM =&^sl)>>L͓_A @yw?!WtC룽X=)^x4ϳW\-$@ @~pdS @ @ ,*|N1& {ȁz*[`N˨8iʃ&tڛ&TO8gwu|jO=iХTc}̮+۫1 @ @@u W @.мdy׎Ux3'@`?Ǝo?}fx8x<~~2 @@ lܒvv/~kb}zF@ <Y~ռ @({_" @ @&Ss|Q^&PBĂW6OG<)fj[B @,%R  @jJ kymȚ\j\uxqacI8jhh CS6Fg1H@ d]aۿ._rծr/V} @ P{:&@ @@9 dͧ.|]VJ˱@5 л [W6s&Ƭ`E6 @e>;ngMܽ&|j\0U&&h/Yei @*X@O @Ί(,J һPLo8aj}Ȥ8t]Ae @ \Wvز}bbU`mmzʷr]W>ۯ3 @E@}/(^"@ @>Z/<ڱ> ؑ q櫚ç@ĘmY @G #]+~ݫg5T@cgnpź )[ @ PUZ"@ @@ dͧZ3۴3؉e\.=%:hb:{r4# @v;[x_UȊQ  P"/l%W5E @e- ^ˣ8 @T@˂ D^J' TuKrTSviev @Xn{?w<?mttkoM-|eǢq_%:jw @ @@   @ P,<9l?"1eDu8I1omXwM @%}G[tʮM?[s7JjұF,Yji5C @p/uQ @*^N,t68+dq櫚5O#N-㢐^s#@ bbqڴ{OƊ_|T/"˿Z+  @ @@ $WH} @0/|e!?eҕK &w88,>jĐ @@~rs OobڍHo-D+o^|Kewz @(Wr]u @ @ZO[8ؙ]~ +]<'ʃ;Ϙ̛ZǦ# @'qa ~ݫcH @b^5K @ @@ ,D @qۋ&D4 ("8%>yZ;)'NM Pi:ci vv/7lYi-Rάk\ @ h  @Ԡ! >y|,? L" 745xUku4~dEh @SؽȊ͑՟nԀ@1xUg]=>Yj @P@q M @j7oѠ7q}B5'&8bPiqQMq!M1ѿI5 @Vt/mttrnT@[DvEC}'nl]ԬL @(32[ @ @|ZOyydgo*#@$0~x۩#S{ȡ` @Tm񣟭L䮕-=w#@"vY\\-Q"  @ @lf)B @Z/<>*&@ @@>n᨝C?JV&e)@ 9glsxaSbQ(x+) @H`Sۺvu/~r{LXW蚛.f3 @TOE+dI @څw΍,2?/0&=8nxۂYqlک}fȼ{3@g @؇+]SU[q +mձ F~ M>u~b}zGq! @T{/ @ #e<>:GW:^8)Ďf+@IDATgP{S4Mݫc @ ͏ >Om I@ d[,x|.G @X@Gi @M* lAok,/p7'S3  @;XmOĆ;eN wre_Ogn @ P+$@ @fZO[8+J;f4N`&48ةq̉eގ05 @n <~˵]axylڶ[9>B!؊% @P'e(J"@ @@o<ャڇE4^Coi '0dP]Yq Cb], @Nǭwo-}<ضjTȾyUK?hmK @Ԯ{  @VsΩkY?}B/&PH;xZqr+Q= @@{G1|U?cE<Gt9=,>u~b}: @*D@BJ @@N˲%ûss8pW2>;[erh @*B`w[gxv<~swGEԭH!mͲv%WtA @ ?/+ @*XeE#tD nC*F`ƔfljG3'Eڼݍ @xvWGOƷR{[{g kh@  Y%4kzF @ jXE= @ P3趶!~5 f!4NƌY8P @Jؾ-weqOE15ToE e][+zE @ @o' @sΩk^7Y}"U_Oc7Z FW-̣}=  @ @ @睖euߺ>j\.nGM7<#;%&q @ ?Nwn[;wwƐ @`XbV5KiO* @{9 @ IO?Y/{H@/ m~xu 4sb/j @3;#qCfӿy3zK ۚeuK[ @^@@ @,O5`.&@7F m?|8ةqMP_=!@ @@_ ,.NuK 7zZԙqa-1rྛ @@1m~]vu+W9 ?f6Xue:' @ @@ ;  @ ͧzV.IJv8llIGv @ @>;>=1Rݍ(FuK6ްF"@ @pMMc @ @Z?'"{r o>4^w⌘=/BMA @@e <|S׮߸iYg; @U&ɓ]a[^)/ q{?p @ @h9ᢡ1O#kw: G.oY0+O  @X੍;=xb |'h}8 @zC@7A @h"鴙/sHuxkgłc3'訧 @ /;X۵o}"پӼN#yWx?li @ @@o  @ ")}dZ]g҈/:)/!PȲ8UMqi3#C^l @ @߼yY|'6ԩ wRZr|x @zK@$C @̛hЖ>~7jPcš'ę @ @ʷDwgekn経w @{_  @hZB]?0HGLwfnx=ҿ F @ @G}0lѣkLJ;K1k5Kic[h @}" '%@ @ZN9 k= Gog0#59| @8Pb~d|ƭԍ lIofժsE,YѽKE @qwC @86><Fĩ >x||㤣E( @ @@wKY P/SUK.]R* @# ^1KP @QuSʱ>5(}g<33n0 @ @^voܴ,rM{9KX /ޙl^c @ @@{l @t 8Iӓ?@Bz˼8i1f} @nx0'X5*@ oV\KuQ]J!@ @@E Wr) @8眺 M<.Gx= PuY9s'ڽPˤ$ @H`㖝qO6ѵN&PD!U7]vG e @O @Ԋ@ǧ>=Vz'L4".|qګŸ=Թ @ P!<~'~ nwMyL/xdW4dٟ/_rތ @\]; @ OM >:4iJ(S_99>yq‘SjW, @U[RGm?+ T@7 _/韄TZk @p=K# @ Pu -l~铩Uמ@CCx֜xis9S`$ @ @@ <#qӲJ*+DVUK.YW @" ^-+ @zU`],ҠՁ Fƍ8Y2»Q> @O_wDw{{xtY_#sBݕiSʸL7AYsM̎W̝o @[`٪-o?ؾ lOV.]t}7g @ PUZ#@ @> kG Xoҙ_lG T!gzVLnuH @"{ſ~Xqǀ`Re(pKamJ"@ @@ + @(S.86˥&u(f6ƹo>4;3F2Pe @jLߺ7oK`Y/^u%;z  @ P5Z$@ @m~0ZԖ@xk㏘ u[ @(+XW\woz*M1B#|{19  @ 0'@ @Z/G͈Q#Tj&@ @$b򛿌#swb(kn^|}֮d @U@},^$@ @RN=BTR: O1iQ__wù @T+7<WpE;q"pq':*^}Ԩ+TK[ @ @*3~#/u_->[@ @ x,  @#0}'wIMÄ@-ylKy- \ @ Gw>6>/Ɵb)FdY~%ZM_C @U%ZN @ZuYOƪRc@)tXyp @ @[5_u7=:{qdC(S<~X[/[Q* @% [$^ @ @ZO[8+,\6:R (yxW̝ @x-;Kc/8!x&y"CM @ PUU!@ PES׺~w}jHu!P_{ΚwqȬIq @ @o_NAҮɌN`FVUK.Y6𥨀 @p߷# @ @ibgvU*\`B|s-sOL@ @[tʷ/|xzYIk.k鬂~@ @@ Wj @@ ,*wbC}U ϝ Ʉ (Hk| $duu]t? I(.. vfP@:$顦If΀.~;;ϓ'wo̹[ô?KK/0.>~U jm(EH @@w8BfԱxr5/ͽxId @Y@{9 @@1S\wq" M8vZ`֦GG @ @lKO>1x ͼMg\%@ @@ (p]O @ #Yq)%)0rЀo9jhj+M @@w>g_yo(tS@g侱$:&* @ P zy%G @'Ny[(dd]mHC|Cś+F @ @F`kg6.Mwt?Ɗg^6 +n[#X:w8 @  Z @*T`7ڱ~zz %v _2N: @ @@o t캇+ {XؘDK87 #WPN @@9 (p/Օ @ >aʛ "SLK >vHw^Q[` D @*@3-t+eϬXܵQ]ѥ3}YE+I @]@{ @@ \}|Y4W>tpc⫣q@m-O @ @ v#qҧ!_`UħYOmF @]@{ @S܅t{Ӕ!'uSw51O0( @ P݅Hu影u H_<[,$C @@A @J_툩w72ٸ(% j|=o'FmX @ @}/ٕ+ox4f]qO,Qfk+Y>}=  @ 'β$@ ''N:<%O&0(~N ?{3 @ @wo|}vt(m\$vS  @(B  @+TyC2-ݕ iLA*{߸gkA @ P0l.~v1һ⩕  @nA./ku뮏e @*U@{  @&$!t#PP$~ۑc/ڷ @ tƏXa^@) d">3OD.+ @"P^$ !  @% 0S}?"̻#Jw @ @V눙?;~c֮"PH;zZ# @E  @P`Ij2ﰱP`W '3j0B! @ @^*?#n,9Hrɗ4=bZ1 @P^us @(!1'9$!,Jy#o9U1  @ @$xbe~џc]KC{I.s1:3 @ @Hu @*'ulLNOZ.aA͵ŴD @ @tߊ΅{}œ(uiʔ%sf^+ JgDJ @;  @&$PъUwkb/xD @(#_xO')R1fkS%J @@Kc @e/0铹H~fj_I*OxƘ'1 @ @#ٕKPLxvr@ &>s~ޣ3AY @ @z ܚqew%[;=) W˜h< @ P ܋bA @0<~R]tz.p>>A @ 'wrG#K_s( M3/H-5% @>P g&@ PO6\Fy@qG*:/flk|C^UU, @ @/#pc7ݳeZD"+̩ \]\ @Pޟ"@ Pɘ >i8(l_`7WVo+ @ @/{8?ǼǞ{5'l.N^>w柊4>a @   @I`Ij2 m 4W>|`c«n[M#@ @S{?#<.(@WZ%W|' @~P&$@ P1'9$,Lf%@u&zqqĠֆ:iE @ Cl\bsk6ﰽ .9L}g--x, @ @ @FOؘ>0@'|ҁwҒ @ @G7msq_qsgjL+$%g^]MI @@ݔ @K`qfK9C`g|GmgC @ Ϭ/+.AM\IT}~-7sb#@ @`ﺡ @@2f¤O"A\}1(&C[㛧B @ PǗ\ZE P2Ϟ>c @(}u'@ Pl#MVI.H߇z{&d ?{<௧M @ P@{y&ymqO0 Sء\SdK @'L @+0zc_/Mn#Xc^SN>(ƎTHLO @ -k<߾ϱ ۺ"H-/e" H @^4  @ xbU#|=#SXLN`;zh|#!kN  @ @E`SGg̺8Gǖb K^`iKNY2wܿg @JW@{鮝  @xQb QL @ @XԺƏnn[EH"F4+ڧua|B"@ @ {9 @bh;f҄&1VLq@@u&_xP @ @JX{ϻ%]z9 9ו9O @r$@(CiV[H˔aR*q3t`53> @ @_7k7li(&ՑI>t_SPb!@ @g {5 @ 7iXu?$yK8hwWH @ P.+n^|G\~] Peo6alB"@ @` w2 @b}┣ҘFS\b!0|`}|CG5U@ @ @*@`cOqOU@R,Arw/{%  @T^~ @L5ݵi̪Kg>L&-Me  @ @^*puc O>!PX KN]:wƥ  @D@{O%@ P!IgOҩӛv{w6. @ @T͝qyW[*#iY@.s4d. Z @*X@{/  @_`Gf<aG3RR' @ @@\8ָEI3"Q$'.l?saLg @vZ@NH @O1'.}t>\qL4#@ @Dw/wK,Xӗs %M(2 @{ @(2#Nj.J77z{& ;_|c V: @ @@O:qok7lIWm @.Xhٷ @P3j @#cch A{ozXn=1 @ @ ֽ1|Hs/j( @ @/\5{A|?S+7"!P ٴ+ nĴl1$ @.ҟ'@( Sސ/{4Hkp|AA @ @O6n?z^lէs@^䷱5󁥷L_W{ @3}Fk` @/+Mٴ?5/E},PWc_u}<  @ @(hƏnn_tm ,J"9qɜ8 @h'@(od_i~"@ Pp_ @@S$?ErK.#0fDSb_,H  @ @Xݺ(>u$hc @@ (w'  @zO`c?3+sUZ~Pj$ d2_<͍uuҊ @ @@ ᖅO>z) E77dNZvϕrb'@ Pl ܋mEC PI6.OR gX|Gk^y @ @@Oog\|{lݵ'OD69aM3A @@w @&MW3a=O*C߸OTf"@ @ Pdw?t|q•Ep*D`cZ6gƕ4  @>58 @@9 xk;8sr+No|;jPq(* @ @,ٕ~yyݱg7T g,+q]< @y;o' @@ Mu2%x]3Hd` LI @ P\vqˊ @ @e#p+Ͼ9[lrHiesO-EI @ " @(rO{mWae"Uxe$=9"{Te% @ @ڙزp~S[}pOXT˙ @ {L @@% MOi?I4URr-@umW?r`E}]M13 @ @2xtn?L3V c~ȥ_3O!@(^Ż6"#@ccki _LĿ|5O @ @z캇s"wB E⒗p L>qF+ @ @lG.K}8uatlNtVV nLbzS&[o'/*{ϙq}mD @/H @'MՈ7T`̈8sQqc^z @ @U˯$Xj"FfbxKU/tKjhUl=vB+ݬgҒ9݉ @(i%|'@W툩r]?N>)>c|!1쌟> @ @zC ō/g߱$:+FϤ/"mJ$Zla@<\#KF 2̥ɠEΗi @XY) @J;ݺ2kR7TV`! 1DF'@ @ @`KZ]p\yãs/4%12ݩ}X:Ws-1gޚؼk{' 7gOZp۬f5 @B (p/y  @E`IfdeBT{U|o   @ @R`kg6p¸ş[ݻsTÚ_(lou|mU_.i!pgЏ @@Zd(N @h?#I~:gA&>>ݵȘ=v" @ @ GK}( V;ђhMbhSEu46Fkc{8/%5ywMM 1Yg,yzSm@/$U'k>D @rZM @*X`^~`@〚#ǽ*ݵmT1 @ @ܿyKg^*ݯ nL[^,/UUe"W7[ٜZLz*pkMur7x'@ P*3ueE e/I;4ICy wx[|GۈyЌ @ @ }x㘽*)D @ @w4ospPCӢnrTWǦ\C𪰙{ZIŹ.rqҹ3WRQ  @6JNo;E P<~R]8/*WA}%0[:2ƎWS @ @@ <܆ڇna@mnnITWݭ6ğZk6t]l*yS"K̚f+l$@ P +v%N(]G~rxMM/ ,,D^ՙ8S?q 2 @ @tzWwz"ڻԘĨI *wJursmؚ5u@s%D4豞 @ VA  @y drJ;ͻ8rO{GkM @ @G+w{dj-݅޹\֘;oMl*U8몓{p! @ (p97 @ 0IoMҩ 0)T nzQ[S]YJ @ @@ ^'} VH @V"o  @,0W>xh jip  @ @uo_j^|ϲY]>z ݫ?O*@`Kci*E @2pX( @@ 5V8rqJ(h3 H @ P7wƕ7<?x|ٚٚnλ[4d2QUs_]kӲ2 @Z@{_  @`x\1;5N!p#⻟=:ƎN @ @ ҧDžiQϮ{8nؒO4ֽ[$2vw/'Wna[ @?6 @U-cr @ QeL&d|kC`GGzQ[cKyN @ @`[dͼٕЃ=(Jr窫bkUcUQ3k?q㌧zMk @#B c&dߥGE3/+0fDSqqkGl;  @ @x@gg6XqcϾvTNhޱ=5it-qbƭyѐ\tGwe @.MH ='LyS6rH7oz"𾷼2CcPKCOiK @ @[9~⧿0Y)ob"wG Ų5Uu7*]$o[:{m! @P^\! @@ ?I ^h3HW @ @xq5W[: ݭ}P\l;0ƖhoUtes;_/ؔn>sf%W @ P 7- @ $c&LJ27vAF&cG څQt%@ @ P9n[_=/n"\bxsZ>0:V榦8-r^6wU6  @Ef8 @(p<\n{Y ~?xP|Gm/e^ @ @M[k~3??. k"vIԤu]GV&@.|k_Kc.eG ! @"P^$ !  @@ = ՛H>ro g~\ѽ?  @ @kǿWH߸5]G JbhSyuƖhoMtl멌_H.~өwy^9T  @W-gz @ ;ݺ2ߦB`WNy^Zvu(  @ @]\3/%ѕQhĠX Xb Q\$]ۑz3/J P>>g-eB( M'I~;$d 4Uӎ~u$lvF @ PX-W^j~9Qz--LJ [K/xE/=b L< @-Wv @3qI1o8@Q|Ňޱ6j"@ @(gǗ7n_[;y\ĘI nTF7Z7kbuEJD\d޾lΙH$@(weR @@6a#y+r1#Ϗ_;# @ @xpʘu=?>]\x}`JaM~9Q2 ?6'8i"@\w2'@'V=یg~23 ƴS!9M @ @?3o_h} Jwj3(QMMqŽxrUGy'*̲?뉌O @ @@:ԆLc;dV@&O'A @ @ܿowY ۇ6%68:TGVWRr$rY2gWҡ{>Đ @e-Wr @7iXu:-n?0Fk~qB5!9 @ @w,YW?T]؞uӸZZ5yKWe&H2|}9 @*G@{嬵L  @"6~ߧ/,v<*j,$E @ @ _l]"`DKZ>(5ywӰģOe'7yc6wֿ?~o]cx @ P^.e @@_ wALj㖷@&CEUU @ @@ dsfcq#W-I+F&1:-lλ$~1)n*ҧ ]ͼuMg#@ -Rq @mOҎ=T`! /N̓ @ @@E tvfs~~o,\6o=#vKa{MU4` n V. ,&㖷.; @P @c&3u;;~-pc۟>* nl @ @T@ǖ]u_,vCiatQiq{4|A`x$,9ǎ ߝ# @PSl: @U`L &Ew߂;߶[ @ @@ lؼ5.ų7-нK贰{v/ͦ62鋵y);؜$yjA @ @/#0fI_4qvF ~qbq @ @@9 Yj~\z]GީUG#wvp-->omlqc-T D2iɜ3 @p @/ $c&LV./x# жgC% @ @2ޥ_?݃~ּ38ÛRaă+=5=}mɜJ} @;%wt"@T'Vyfٹ\ H6o?jOv @ @ nsq_\vñyKgޙ7&ia{C獦. TUUhZK\iAs"}Ȝ  wNG @@ ڵCW4}%T^2U/NCCO @ @Xglޱ7սP>$-lw(@SKkx=o 9S }?eS}Z)TDE t @R6Mu_TswxȨ8baf @ @,177=] x}PyL Ƹgіxrm\u]Y;j: @   @+vQ۴mmd2_Aw5Uh @ @gcn]\q}$Zwb5[k),@[~%0  @ ,ஸ `z @@ ;uttu^[챊ǹ_{q& @ @}$pO9w-Եǐ ۛ꽅GKc^<0f߻2F?KSn-Uo]z  @Zq_  Pm'*u/{pB/3& k)$@ @ п7ݽ,-l'nGÚhDcw̶+MqۂMr]FWͼyMgeM |#@T艧>ҝs+,u>?5U8 @ @[=_~#hK?L ͻJ6\_+SUoZ4'J"ZA @D@{AMJ(n1&E4JoRUt 5q޿Mڣ  @ @EKX٣G&1fp&{McE*DmCK߷*\("XTy4>a @X@{ @b=qҝۯL/S58c Rd @ @vQy+⌋n;zG# mJb;Mhiik7l-Z+H[2gE  @J@{Q-` @h8}]FQUH^J'iǎF(u+ @ @@{.θ{% lHbI4yk>5-KQ`@}]<+/Xs֧cٜ   @Q]t1 @h0irD2SD2|8"qkQ(l @ @xbϟ{$ *E hiA*GH2%g^. @T[r  @^"M2-"\qv5_7f;-&@ @ PO?䮸G3w" gbHFӰ[ZbμrP t;8}6  @JJ]uI-`  @-0-6$푍Wfhh9| @ @@E Yg]yo\bμ #vO ۇ$M4,cX2Rj} 0y霙`\C @{[0 @zKOyi'֘)uVT @ @e"3οz^b-ygUS1fp&F&{nV@uuuZw-XS ˲WJdo; @*U-v  @u ?O__>6||kC @ @l ɝuy۽KniQ{[Z޽{ZZZbμ+ZU@.%>:+ @WK\=xj떭uH\DGba-=- @ @ϋ{?#ZG1hIb!Q7 @cÀ3= <]YyvЌ @LBJ ##Cvu}_NyC B @ @ndqqǃO(M/7lC:VvŽUT -lasOϴW!@(W庲"@m>=9!m Tg21_wL|6;I @ @?sZ\lHb!I4{[=5- #DmCK̾oeM36e+pklz[7ʖCb @p'^k,C @^SFl[C` q^52֚ @ @(^+MG݅N%筍M]}7I`^u @ @RcewϮ$:;yO56L i6zhEqߒX]I x8g-|IXw'@]`SvT'srU~.p{^N:$jkw}0# @ @(qEy 3җvO ۇ7'x=o7 @Ms'֐)k܂lsӔ @ܞW@ @@y Wt&Iۣ<3Uo VYrTw{kH @ @Wb?}vÖ箩hZx{!QS]]$ @ @lS?/xpm^-I>$uk<$P[SOoy SXb)NO.= OT @;#}g!@qdO\d 38sc^SDQ  @ @n8;C7@~B%޿2AedsYL @JH@{ -P  @h;fak#rۺnAuqW  @ @@t~E;u@{ M[%%VǖpϤ8vIy< @J_|鯡  @ }┣R fcPח @ @C੕/ljH_;_1,7J@1z#^Q 2X6{ֽ @ ா A( IM:wC!7gi1qƤ1y@i,J @ @ϽqsgUcgbdkP&+ԋ+̛t]8 @Y}? c\6wu:V}og8='!js @ @(rk<_t{,{f}ޑv>$ՙiH@ $?lusU%I`uq7/3~4 @,A Gk-~Γ~ٮK2y3N;*N8/  @ @"g?5xE61W Mc 7ܳ*\9#XqyK oT @Pޗ&@@ۄyy6\6E_=6y][d$  @ @rx ~uc=*RmH _1,] yG%筍M]4צ2g3neM(]wv"'@*L`)%KҴ*,u)ڱ/{g @ @@ǖ8;:}etLlM"Nwn(gƆsxfuG9)][Ξ5g׆ћ @? @;)0z$I´{f'Э?|L|wPJ @ @wk?c3N}}! inX* msML}7 @P^us @z 6q"]Jj:Ŕ!jkl_I.W @ @@) 31[·Q؃xL Q7 0Ll6̥I&9a @P$U x @=q'\O[m*O VGƻ޸O%/c @ @x ok<\.jsX7DmCk̾oej%%ٳ~[Yi˖ Pz^ (51 P!ٹG4{(X?1q~czSs @ @}/yKgW7w=au%cdbdKwvӐZZ{W'="$'.=OyD Pl^( @ =~7o.L}3%޽5ұ!/ @ @B }Aq y]>51iq{uͅ@ssSX~ o tF&9i(eF(mS~'@PmSҭ~w}UB蚕Nr)]]uW$ . bw]YJ@T,XEvWdUTZP pSo_2s3wd;ߩޔ^z@w|1u{ҟ @ @@Aգq?h^NhKbIhi̫xNڈUmyvެY$i/Y @X@T/ @HOn̝QT{uljo/ꟷ @ @%߻fs1&'1m4$@ /xb[SwyӸf#5˗^T3K P!v *dIT@zrғۿfZW0Lu{`}tӖ @ @ lزm0313IDs!F%#QשM޼f߮L%H XB%@W`^ ]<:Z⫧3kTu"@ @ 0]v|[7lV@B$@ԄXfI @,нؗEd.Kcl,8V|3:+sgN,f$@ @ =>NMq=kg%S2jّC ttv7澡1'0dׯ^~  @U( UJ @@\xQ$(kEZ ~ఘ<ә @ @s l:~'퟾٘>rI;hkk;Wƺ'.3@$5Q(. @@E' Pv3{xd~\v _c|⽇D{=t!LN @ {]@|++b-9܈a\'f.s7  PƆXT}zͦgؖ^SQQ  Pe ܫlACT̅Kfi(%p{P_W)C @ @_!N=񮇟g{%hkr;ٌN@2LlK6>$/]}-0 @jEB<  @pA$~V6A ,>/;b=A @U}qw"sfhHl3)S;mp匦!%hjkZlC@y lJzC˖  @6,:˒(Ö+p:$$a@}zJ?0:h2F @ @,pMG/i<ئvJbvZ^ɫ( ή'c`H{Y,H @`;W$ka&nPW M/1mRGe% @ @JqoUk7 uyuӘ!Z- &6ps~SMDa5R'@^`֡>I3MP)/?hf|vloI @ PEkq劕X[S;OImմ$@cƸwU~_\Ŝ•]^4&@[Cd: @r~ؒy CZxwz@46Br$@ @(#8K/3 Y]&bLЕDsn PL&d_oDD_P{afEpl0 @?g~ @F焝#;t]:Œhj8 ^ozjXO9 @ @*I;x`Sy=#93XW7  P%Id;b/|Q`v}0 @*k.g @v ^xCI'3s(ݫD =%>VIF @ @uG/i|F6=v6׫1SjqK9W3^'c8V/ p_C}^ @v# ojD @dV.<%] R֦G'+ @ @WwwŦ9OV0kB&K'MCT@Ggg\uBUR =43y  @ ؒ(0 @s\w9=v]Z928O_w @ @c*q7ƯV><ړqr&1jF#rcl6T39KyO2? R @L! @v^qpsC)q T@[[k۾xB"fniޖ}+a.S @jBEM,$  @s<Ʈg:[*'h @ @T{ Ny1c|&d.W7  PMGj]~ͭi*O>}2/}l7Uy#@E@{QMBTǴ&?N?TR9+?]ԷMW. @ @c,no]LmMI2%fӘ+Pk75ƽ62!Gk7m{÷i_' @VM/ @@OMzp8ciWA @ 0ląe|Ŧ9OI>rr{nqn @2j3I}]6ӗ5 @0, h @>nc.J7ZK])0R~撃uGoz @ @@~jC|q}5rWK;ODKc^4&ij@IDAT@H"9[yvb||N2%@V P)/|dk"[I>@}ZSzI @ @`{㬋Xkݔp ;Mܴ4$@@mݺm#ߺufws_  @Qؾ. @@m t/\TV@sS]|cGơ vB @ @`Lng]?` ^+7=ݧEW{ttU/;\MQf @1vF&@P{Ҵ SR;⢏{C=5'@ @ ]q_ gs/lo>1-pw @@::{7ǦśLe+DS[zJ(0 @@ 8LFX @@ t/Z4s/2[`IO4ϝZG @ PCW޲*ޚ~]GӺޤ~]J@;S[7T&Q tɡ]sߘ~sb03 @hTޚ(EKNOX$Ӕ O{q̜6  @ @@ nn{Wm,Ҍ)cd_ڥוqB#@]XA @(0I3SS^hN|؅6g @ @ gKE־G9}ΤL0. 7}sfӐ%ĆޒoҲ84<"Eyq?  @ @U*pOI\x2ߚ)hnȫ PB$I/ܷQL  L @ݒ(@'̈8f:V7KE1r @ @@s_y&gbJۼ9iHH7r}YE%&كZtMIf7) @| ('%ف釤ŝl&ѷ/"@ @ P0z8>t ޼)j1-noH] P ]q]+{ =]}  @*PSE) @`&|RGs}5ٿھdLzX/>(^  @ @L`+l5;OĄ6vsWӒ/1.㉼M(D@6Ve|l7W @@ ŕ 6f;8,^ZV@}Z-ڹR @ @@ n#qcͺyE19=}nzj{SrӘ"WO8sHveKk_/N @\@{/ @zN}⿒H^CkMT)= ;J3 @ @@'zK|U0rjLLlw;kf>[3kC xbi1HſtG..th'3 @|׿I @X 9F$;O/sfy*: @ @u~n_,1.ڛWb !gwƚ'bp(1cR]Q`zWA9'& @@[| @@9 $݋9+ r J,abkZciŝl @ @U)?0sorα>L̞.7=nI\OX5gޕ\7L5%@O{D茋?iĚ] @ @o7G񓟮lNhé-Nm/!swh_{eXD5gI7$@TJY)q @^tjx.S_S'vTT܂%@ @(?3wPE#'49S;ݶ-UJ#wG7&@E>E0 @@NI MIV`Ƣ%G'$g.Ef_iq% @ @!p3=[WBZej&1Ԁ@ssSܷ6kZJYlϾY2 @uUJx̅KfOϵ!M,  @ @D`h8}8+=} ==k92C[iGZhjlld_ڥ!@Ԛ]Z[q @u { ݐaNϊ3o_ @ @S77 ~]%]Sۛ1Ԩ@C}}XN6g^ @ZP^K-W P݇0#oHf0CME;Ƨ[-  @ @#F|黿m?PܞMC@] -qDjW!pޯk@ @@ A/A<yy5S7S|=  @ @Q C @ @.= o-s>Kr8}. }Id;_>}]w76l;W}JNB @\l䢤  Pg{# M@)W^qvRN @I'33kD(@XOlHZԤ_N-XQ @c$(̅ߛMFaR {o/2Y50/ @ @R֭8yژĮӒ9EZ+Ic\g'Z~5>q @W O@ůM>}@'i8qY. @ @y\8 7S=>Y3% } iܷ{'Ǭ *: @E˳xL D:23/cfAZܾ=K @ Pk]v^7G̛RyiLhlkt{@+kW+*+l @_NiATÖ goJÝP!! }׾# @ @nw],ܲXnc^NlOb)˫ @M#EwlC6g2hյ^ ܭ$@X`N{ doN?)06Fz~W=F @ @*.#^rg  bɘOt5g4  @`ZUw>}̥rI`YF'( @(캌M @&|RGsҨ]^KSh>wYLB @ PkڏKOm9=)y2ҘO/m  @@qZ;ŕw(r/vy͒on8L4 @ (p^ @@,XpLú$yI$" }A#v/⌦"@ @t.?N9ظ?T'1{b&̫݊ @]iz'Ul&[ܚ=+MD!@R 3~ @A yō!~=*n/. @ @-i@,NOm_>{ugb$ xԈOQ/ Y5OKs-Ʌ{  @ELK>m1)neZ[˖ @ @`ݻ.+b/5$^m Nc(@__̟3>~v[dKɋ-~bٳs֖ޕ+*#@RNL!5EM`ޙ$6B>n)E< @ @(ol6κXzɝ184s.)vjh @@ ttvŏd{-$Ǯ^~yl  @j#SM) P#az^H#q95T  @ @F+f8sb|$MDKc>%@)О_yǓ14Ƚ&@ /}ؒc&MGn)F,WL|#}gk"@ @(#.?N9ظ?'1{bzyiL-W޹!}"^G5ڳ|d @X(@w ;GvtIH.X葱^3=T @ @X`ց8c=>~j{WۧiK#Й䮴}P{ZA"},d4 @$`HЦ!@O`AxS:IJ)n8-nJZ6 @ @J"p}qvm^1''˔LMc Pq#E#'8ɽn;ppx|;ѝ P4E6 0Z iʹ7_ocWy(-n?`Y  @ @&0YK/3s7)ir&ue3 Pq]10R_Ě܊ P !U#i3]\j,L&yqоk,s @ @#&=IH{W{S{Ψms5ӎC?UpvVGE23b~~+  @@(p&U={_ @ @7Ż?sM^/|W&%L=G#o @&0R;6PzfklvoS5D  @Ht @C;֟F<(Q@E6  @ @z/u'}?ӽvݧ+n1Z`cooIY(7]AdET @pfi@Cgɱc.swۗY @ @l6m>,.y ۔e>#;b *N/Zq xIsg? @ .$@YҴi?wūz~]  @ @`~w^ݳ.)Ʒ%猺hiŘ @~_>{|V3TKtoƕ^J(;<\%@'0)ͬ3 |o{ @ @jX߿'>?0ٓ2=mϜ4$@9 wUwl6U`G=t9Vz"'@*_NO寡  @@ L;uɜMBy |^Gc @ @شu N:WMDg[yiLhj늫"wW<>y{5Md,Q @,T6f;N3=x.A @ @~'ݟ&~SZ.ߚĮiq{C].!@G+KVX_'\vֆ [ @@ d( @#*n5ۮHOz>o @ @*ƫy'fb۫A: P[&qB+%sz @44(Y  @@M \HFƽb߿(Ãjh٥J @xV}g._zW ehLKv)ng 0Ã}1{X趱!03]G @@ (p/(EKN/q/޼d2n:4 @ @^7⑼ךĞ3ꢵ>S^p @B ibc Zc߱6>2Qh @@ ҅(G(il +Ώd ; @ @*R*Nͱ0gM Izn(@CqO>C`(>^SA}?JS 73 ޒN2mL'2xYU┷uubAA @(־й7_{^Q4Ϣ7-# @rH?jC9%x2;4tC7뱛 @?~"@@`r{ڛ7C3c>4;. @ @'㝟:__\WKvD-<@$9?S )n8T5 |Vo @8lEX Zރq;Q^$: @ @@Qsկ'|/Yس[q{Q$ @`l6ڒͱܮQ[ 욭k΂4TX%@*T@{.  @@t3I$x9z]0G? @ @/0wuqYck`49#&fcsfӐL`Ƚ~KcGb0qGh>x3e՗;cKޞމOc2xI_e @ @J%jSx(:ث.ښ1(OroxGc"5zWsa;D5R'@c)s|OdLpXBfuşxYL^ @ @Tn_}ذ/u%LdܹMc PNuuMXF`h8ɾeKJOK@^`Ns÷#O,F,'S;zitOWNa @ @K/3θ ١_?_C @F#`h4j @*0}1>k#oTΦu4bA%A @q?]WwDGەyiL\1~H6yb[G*&34c g? @H2-_NPܾ}e߻!G)n/  @ @`XT]ْ f)n12(sz&&t6,M`Lå PMDԌYj @p"73Gs2 s @ @P`Wﲸ͆>.fd.n @$u[_hhoIn$Xrvu'); @JnN P{;2}7Ĝ4$@*000h5O PUGuޕ+n$%F]QMHO`cwJ2MdG >(:x?O @ @۵qmʺ)=uL75 @ ::;ʻAEUϕP5Ɨ+ @ EUJ; @7~^UD @ @rϟ*V>қ{$vKOn\4&@$?نy"՚eg_IJ(@z 0j+io&+cDI @(\x/ϻ{|{P^1 01rw'=ߞ @."~&@r^I6yC4Hnx۫_P  @ @`#~++⽟&lyn;dbΤLc˙MC P{P9J{6gGH1/ ]]|: @c ؜_wj^L2W2K @ {?}ʸlsWw&Z'= @@- cգ[k1Zyj)i @TXO @yqsCɭik"M G>nQ4Nd H @Q_xbͣЖĮ2QyiLjK Xq߆Jf⨇=K] @@!B @&)ې~>W ]s쀙q#\ @ @5#pU?Sl+Y5Qe{^h @H$zZzkTf~bnh#לf2( P0L4 )[5ս=58a^f @ @L`ph8N98yץwwQg~ @K Ƹ̎jX?OhTd@[@{G*XgSim @ @jxɭ,竵1bߙu1CsFӐP:cδ6"U,Ii>0V:K@X jL @{443շhG_'Wi"@ @x=oyR{^̛ @g):T>k;oT]w[+>  @EPV4j @9al8,D\;Llb㟫 @ @H[W?0WVs&e{یyiL<@KsSܾr0?koT`&I[*>  @ETf @tI[oNnߣrs 4>j= @ @*ƇpS|Q]&b޴LLhs1/8  @hkkI+U & Vx  @P. @ $}*nV>ov_I  @ @ro9Gy77D3Sq{nZ @ l޼%͍uvվrec+'d @P^*y @ pU@B'5su#@ @$=T}ߋ|(;[.Zܞ @@^7nz9򂫨Ɂן]Q! (=ݤ @fvӓ/H#T5?_ǨD @ PY7p?Ol+IC& @cھȎn] ꚽޕQ(ٛ@ŻDܚUA lGLyљ @ @{/yE;gR&@ Mc @ m];GV{A~ۊC>l%=Pa>UR#@ @pZ/~4MĄ6Ә(@CKW\`e%f`hYE% @,:X~Cyat6'ךּhkrr{^p @c&М{ \jgFv:  @'PW~!(LWӹݽ*S7şxYL93 @ @7SߜWS:mz& @ dlp1<8$ޕ]LJ\A @ t~Œ#urqg6[1Zu̟;ә @ @D}q7p^̙n慦1 PT榸c`<_yMV41^MD3zyG^`cCCΤ}칋>C&wᇏP^ty @ @'0lz~:3iMoDV`۶xNM1r*Z"n'L$E-=o2 @@u kodzJ68C}gG @ @T-8rݑֹ|5G33۝ܞ3 @@I6m={vD}/%]|h(>ڷ @b| ((̅K^IviG6b9| /ݳB @ @Z1pg=I]- Ә(@__̟=.X`LvQ׌%@*FU,@  @@afrNuC+F+wv~?I^9 @ @Xq8SWmy =#]f"c(/7  @hlk\_^A`Iyeg]V DT[2 @lǍH^^pvqa=6!@ @B?~87p^͞ Mc @l;{7m|.~/;Eg @b2  @Q w}|e񠽦Ƨ]WIp @ @ dO㤥U>rZn;(n^ @@ mv(ĵ}l iݾa&@*U@{  @(.lewX`8â @ @h6m}qewH{Wc}331iGT@6P4?LkEvҞHC @@.=nl&.O6^ɗ)]H  @ @rXncƭ˩7'׌hiTG @%0<<S:3.ld3"nw @jKnVml  @&1[Kfԛ⻟yY˴OR @ @ZGI<ԶFNluZ&21 Pmq}[b"\zKf8U/繛y&L5%# @Yg72駹r]A @ @]G7?ovUw @_W  @fu ;0CU~;ſ-鉺:qUK @IXrƲ +hݧg٭4&@V 7ɪͯۚпe]R'@5!"&Y Ppki۫`'u*[W @ @-ac_+.nonاNq{mȞx@f`c3i Zҧ]:=U @P8"@,0gī+9ZMW @ @*R`ͺ\޳6;Y-yuӘ Pٴ z|ö;swk֟E5R&@HX"@M`VϒC=oy2[-nSc]|/'VQVR!@ @Զ/Eϋv] @ @L[W+DlΤL̝$^ @jW`󖭱p7>AUoAsdN:*K: @@*ݯ"G:RJ*O5 e @ @T~87p)%i=ּi0+g4  @+ۻ1g<صiZ>$Wʈj\>-yId_JޫfU#ŋǶ*ZV @ @5*088'y]|%PYnjLLh @?Y;UmW ꜳý+W^H_`~ @@L_d4UW`ϼ<&ocB @ P ?ʸʤn1 @8[n|wT}$sCκs: 'y. @,8! \核iw6WN9\q\H @DGy5%̺P^.f @lLi1<1^)gz @$VC, @`74"9p]u)SL ksgM*E @ Wxyj哹v}Iݝw @@choApe1.[[Ki$}T! }u]o @3{x$^E\rP`dC @ @S;7X߻-iv&1oZ&FBp @^`}}1-F,@ɞ]s[׻JY  @B Z$C,0Iiw(ԦC߰gpC @ @sp|5@IDATCҫ\z} 5ݬ5-4&@R+~[ g<|e @& @ WӸ6W2;݋F]? @ @28;ij˫=IkwLP @@lTɶfl$ɥ;yLWd,Q P ܫpQD/ݳ^YN{͝z!P_W;I˔ @ PeC9t/׫.cLLtr{f @ &п1ڱ`,49XA @TPo(@K${iD}I1y&u5Ƿ?2`c @ @*5qCznLt Nc @@:1q`G5Tv{\qK0= 0 'M P* ifT[XL\x1sڸl4 @ @ n;劸y̺hkRܞ @ Oԩgeg9J  @(p> @Du-_M;ϼc0!  @ @!ؓ[o>uyMژu1R"@([cүF"hf3ߙs Fj  Pq6 nǽ.;s2@|m/K# @ @Vq= W7  @$0Un+Ҍks`?빌OF q4 ;eNic\ԑcJ|G]G› @ P [Yģ5⤎?Mc @@7;v}Z@Z~ެCNif02 PHg4G{ mLgm-%ݔ @ @+pmM"ԗPӺ7- @d٘ݓ[J2IDcn9~LF7( PP?,( @@af,9$qa:jrW ԧ'/]fOTO @I˖=5?0W3'$䴸=^ @R`xx8oG7Fopݝ-zWXV!DB՘ @ylt(^8cn!1 @ @)KX7Ϟh/On  @e!e8d~[bY,HAȞ:r\A2 0fvƌ @`l]fm(:;_=?^{e@ @ @r8;_))r4RLL*g4  @e(wc2LHȤ뿵ӑtn @@ @ _oOoo?SཧƉo/2sDػP,ɖd˻%َc'޲݉5R-CiIl)[!q $K _[P;$,$De(e Il{F:g7ϓyh  @ @ Wէㆷ.rG%{hm%zC "@F`ݱWX>2C26괭la @~oڹ6w;K%dxۮGD_oW, @ @ki\zG7}=E5;^j@%fjng0 @#1wV񋑢g* ߻ov&7( @`*>"@8j;Ǔ;a31%hT/ [) @ @Jx5oI۞-u`%fwinA @ W,9v(kT M߰K> @"}Z-J_`,z9uo^8q-_ @ @@K ;O}qgˡ-jtwhng0 @D##e1FfU>w#@$H!hii\MTmm9k" @ @_gĭ_qbgȚۗV+׍ @v0;klN;/kbAFZM !@2 ,?{JKY \K`Ӊ㭗^tNF @ P ܱ;xyw|;X_ xb3 PzJgo|w#PUj#$ @Np/>ȂZZ`W%{&x,73nt&K% @ @#y/wsI?u)*%@F =0k?Y]`v;h& ʹj!@R niCK%V[bk,{ @ P`'~=ߝĺm &@4@3 fw4KI^׷:  @@QEID @n䔬wd O=gcaET @ @@k|?@ UI$4  @L`|G-6Zv[N]~귆n^  @@4ӕ}OXJ6V"$km]eͯ @ @@ӯ@?+$V-\l @V`=4m}-WXqmN5kW0M@{vD> 2mFHVLM\薓%O8+T @ @ׇۮp*lJ5\h @Z@`ݱq}_ T%'c.յv+*P-jb"@,t%4^5Jm f\*% @ Pjx\uL4/\h @ZHFGH Uݬ&K{}mҬ]@{wH~ tκ/M*7e4]q-VЌJNJy-Vr  @ @~5^OE-~J in/˚ 05i7漞jܵ/#)hj2JNMOy[s @@|b @yjcl1^}8LNV @ @x)sʅ蟣^~!@_tE[\wV$~C/]eKC@{s* @$K7] %IWqqWd @ @"w}1~g"q̢J,9(( @@wCN+Cr<@J਑Ή @)Nz#@-+t7jY&)5mUl-U @ 7↷.ws+Ws{?5Fhȁ_4;~ [C)8w_-D,O 05IV}KܩY*X;߹):f5j q  @ @$K2kpsrDK%XأN  @%:=rɬN7,ߴmEե  P`  @@ nq~DznTԚhě_5nMU @ @J$p[>{sed=53 @>8zA;J';T>r ZN PV e9y @@i}e썥IX(pÎ3bE @ @!pٛ>o~Wr%Szf4r3 @;gՉ?6.p` @hp/Nɓ)py-KwϕsyLֵ @ @Hӈ|5WrF+1wƣ\p @R`hh8yC3 ɋ2d*G Pv eA @@in,t;O;-&ڍ @ @^xM_ϕ`5Z~sfz' @78ٓo`a*VJ4@IP(ٗ,1-L>Ύjǒd$ @ @ q-O|+׺4g'ܞ ` @Ug{r5P G~PYI4ܛlCCەlڋ,Wu? @ @4X`l^vڲde'jnf0 @ ǚEj;fF xKN+LB!@M( 7UI 0$,NLdp$u񈳎9 @ @4P`4kn?7>u[Uڲ\$ %0vg\ў}W @ @@{{YfD0Xor3 @`F qGL݂Vj@R^uކt% @@}nIVfe\  @@4i7B<ҁ47.q KglSOXzB @ 0C{F≗/hnNn>{( @C ӏJrFW gn&(3 P + @@{kV_RdyxOp @ @H`{ۿZ#;}_7 @e-'\zr >X.ߵ` @ '-n2JlO*LF ;yn @dn֟}Tf/s+ʁx?$:}WuO @ @@s |0>*힓+1CH.8  @ /06r 鎟uJ^q׆n+8 @ 8~YIW %oHg,J[bNoWs  @ @UƲ ^zs*9{'ܞ ` @ 1k$m}RMxw. @*<$@XeH{T6W<3li˗ @ д4.|~/W_6w;= @SVuE[7kmxW=6^,~!@Ⱦэ8⳷/${q35x#VE;9osdE @Ds>rNo3 @###q9+eG+Nm~ 09'O(hqj%yMF0J[%񂧝;(q @ @ji;o%*m}}%4b3 @v GN?MWoz 0y 2ZT`K-Z~ˮdcNoWkQ @ @x+?۹ʩdoҭ˚{ܞ ` @&Hc~H,l d-Y^дS@ Gg @ڲsN-euZRUf)G @ @ <5w}蛹UszArL 4ZX<3nc$jهFJ)g?+e&@S()ĶO`,jgY/)_2xG @ @ /|'|IsL Єûcs))Ioڶu*V)8< f@46e}=Ί6_XӔ( @ @t/~ӧ\yܾ}Nnf0 @@ E_K86kn߭Y'7  @-#{||_ۄmo&KI @.պD4Q[ޙʙ$%T3*T݊%@ @QU|L6ܾGs{^7  @ZO663gΌ ^QOofs  P?'R$hZY,g1}6%7_ @ @@/Ǎ\V-ܞ @H4bV3NK$H7|'v%N(Yq  @tg|p$%.{uE<54 @ @YoF\[#u;j~%vr{.4  @Z^`߾i}o; Yv/s  qJ&aJ}d{7Yq3O$q'e @ @D$&s#@ @ PvNfD >eIF @ @Y'Nw/Pysώysfl @ @@>ƶW|$kbN>qz @/b`t $IoY~׌L @H-,nȅE`ץiiYܢG,O{plX?xq @ @8|8cdt68J  @ @#)UT$V/\l @x@ݻq[ lk @hpg @@Aߑ]A;I =3gŌ @ @@#x>k$,ɚur3 @FŚ݇b KjKO_1GV 0g8ؖ"@b ,=g$җ++LV5nLvq @ @uν9&sՑĺJT]f, @I iKj1:K Y[XYɆL܊ P]tԜ '@ @c+n~W3&۫ \n @8᡻5p 0'{CGvhMo!澫--g/V'Cpz{ɞ mJʇ" lK @4X-ĭ\eWF׌\ &@ @:_kƎ !Dgũ__I[ ӽ'@)lѾ)_؂G,pEڕ8 @ @ji_~kr5u\%fjnf0 @#-mUO-;B ӂoQn ӽ'@)ؼckStQE7.>|]]b B @ _yxSXheݝjrL @Nw둮甆^E-sߔk1 P E9 @:w{GSE*WFT+t+` @ @I \?Ia-kTbv\p @ޡXQ49i9SE @@th3B8/W^rf,ӈb @ @B5xrQ}ŕ;Ks}eC @`j4}YڅV$v%  @$KQ$@#r4M.;(fOS~oU<ӱ5  @ @-/|5r; {43 @@;nPta,2/o @B hp/vH$D:,vG %grj K @ p0[q_~:sݖͫD &@ 0CwǩkLJ@زAu+ (n @~Kx|(T zY7{T-g @ @_ şcQ>ؾlvO$ @E}Ά+$j[vd @@hpoTܟQ[vɮs_=v]eNRv @ @&W~^nQoG/p) @X`tl,NZmULbz,aiKH  Pt2}GG$0k(S.pqy|]  @ @Z]Y<\Xe\h @&=iZݲG"&κ#a.(w˰Kr$@<{E5٤idxY3M&X @hIox{Gs?+Ջ+82 @S`tP^=)X_fS; @ @~z׾x߽?Wݳ:X_ފ5` @Y MX6]iy҈l)yO$L%W@|[6IO0^=? $ @ @"ghՓkLq*~蜸2F @ 0%cxK?_s7Ծ~3` @E3|whi ƫz @˹o&@$U ٧1G-(`fR"@ @4_ WI6;}7 @J/{O  @ @E^\OEh`N @ МsFgf{sUs7w#@Vj;^4-;ˮ=IkڲiyM[ @ @E7~ۮpr6qjn @4#q-y+[IG_ּ婌ZQ;j&@@ xcVV[ӕte<=ĩ_MJ#@ @ $pۏW{jvW.vI) @J*0<4';ٷt:e--xh*F6v*)0gd-gfU;όEz[  @ @%ɗ?&s9#8  @J-۾?K]C+&X  @47ߞ-% "Ikا=8紕MP @ @w`,릸=;=mF}*jR6c  @^`dd4N_ݝ}wKiNiVz  @9%ٜ*@{5yEVܖ) X0+S% @ @/0^Kk?_Os%[ͮ NnȚ @h=ᡡ8e+q͊\eȟhp @ nٹ%Mi-E΍;EW6 @ @`j^Oć8qJp\c.8  @4ʾ;ɪjr٣cAn @K}'@@ _kF7@9+qqqʙ  @ @%x;?빳>vQ%ܞ @M&0::33&>V*,tɦRe,Y ;į P߹3tM9fDž @ @` oč\_=WrÙ@ @I㌵}MZ]&7lpA{V2hv ;#@@ ,ݸ?+MXZSt3{fGSר8 @ @@nDilIbOs{>5  @4to,Zov:YǬY @7c'(@V6K$J3qqq @ @ ~هblkyIe\h @h8yeW$>[Ot9P_],J;/Bӎ3DRd%yųY  @ @ Oin=GsۙŕЫ` @-%044gkd|&C  Ђ[pӕL w^kJ&~٢+ @ 05w '_]r-5#b]% @ pP؞X<׵߃"4q[?`iIR@! @ t3\6%yZ?X @ @8l#+oo\1ګіF @C IGud CYJnչ;\ @4J PiHbR:c?xRc "@ @8<4;>T+D*ٞk @8W(]MtYKZZ@{Ko  PZzEd,wl3A @ @4P7}+^\.3 @\ED٫/9%JY [ |A`ץI 𰕱8 @ @(o?_νŠ;Ks{n8 @G`| GuE]ʫJT  @4@(@RKdyVK3xOaA @ @O~?/H|,@&|jF @ wM~/@M;Y%G~)S -0yc":IKy/ @ @gC16Vt$V.pi( @8oe 0.Y†- p_[ @ @ _c,&p#@ @@C*cᜎ!xYd5WPz(AuVWdAX<ԁxԦc[P @ @7/?|)V-.= L @ @`cqʮ۾'=iHغӞ @hpw @ nٹ%;qӗt̨zzT+f, @ @`qe=n/E:L~  @&044Ӱ_ %7np? Phu@(eW[kQ ٧"< @ @5;?H8j@9 %@ @zw~#ddVES @d9Y) @`J|VDz,f#x¢x9k8 @ @ν>{3#Ջ+ y @ kzW $k!k B @~Xl& @@#6 @ @A{ƣsӷnH mt}9%Viu @)_ tx# @ @4B຿x';+ݙu @ @`Ϟ ,SZߘ2' 8 &/ddýXٴd=3cm,L @4ȷu-W^ g @ طg(Vw7$ x6$ @04) P_qvFQ/zIvF @ в՟^H| {X:Ws{>5  @h@Y0*tk$nܰÄ3#`Ei$O4Q-vxe'@ @212:$V-r' @L={d-E`O;.K$A @ xM'@#L/" YJڋψ>=U!@ @ZC`x߽?WoӬDŁ &@ @`Ω[JG(x0 @fO@IDAT41 pKپ2;;߼8ѫ|ajխF @4@-M~(rZͮ˚۫L @)jqH|0wJݏ`dJ". @c˳y3g9S+?S @ @- p_~:>o?]%9CHn8 @rqھ)_ׂ)$.;{Üm0 By783k/:-͙5=[ @ Ф۲V.DLy݌'@ @`:ҽ1wӷV* fL,Π @J^ MjxUӟ  @ @M$/0|grWxvKhn g @*0:6Pi݄|mb4YD|cOra(VċyjT @ @# E/prJbv7 @Q`xh(jvSoќ+7npA{lfNX&I׶ZeϟzbZ>˛ @ P8}W0~1| Wn3",D\n @ P,]1Zd@k~q=~ H #ooΣ; 'ptO<./  @ @,W},;s0Ծ6kn$ @O`q:og(dC.@4J@{d%@,;¾wRefv27I @ @(NW-DOsÙ@ @@! e=MRKƪW^w @ hpo0 񶶿~=~*2|'/ @ @@>o?_sdNz53 @ /6kޤNv;L-௄F O,;ң4َ3;q7?Q @ @J"pb+>4WƳ8zK9 &@ @{)ExLwERZha񶱗fhaҔ O% zKD  @ @Eػ,yrfI%r3 @;,O-izm4  @`J4O) К[/9-;Y}>u݂xJZ @ @ +?_\VYs{{54  @ P*4Mc`f*7npA{iҕ(Z@{O(@Ki'Y*Ӣ͕*( @k O~7wU +p& @ P:q9˻E^gh&@)>#@@ nRYVwqq~2.g @ @@>ߏsꟓ^L @ @mbVJWqܒ*M(o  Ptw͈H+zX2of<1' @ @m?mH\fw%bK7 &@ @uK_G3jWH$@i.4[.p;j:k/:-t5C)j @ @L̫?C{FrfI%r3 @ط{(Z<9i*ҋoڹT Li8W-;dn Qg.,AR$@ @_` o|\Vhf0 @F MuQՇ~˱$ʒ*;'o\`,?RW4[>mxO&A? @ @x?|>rZn g @M%0<'XT55o1[wxw~>wԁ$hn g @-'О9-Ww ^.β-_(b P_~$ܢtxԦc[ze @ @#"vpK4W938jK3 &@ @eFG=-[ O޾L9˕-]b P UmM#ɶxWZU[\A @ @س4οؽw4W5@tx{.7  @hm᡻cRP^gAO[5)E(l4  Pd,?-&e=búg)= @ @@ ynMkT¹yԌ%@ @ D…>qMV>O  @@)4b$I ,ݸ?;f( ` @ @N+WS3Fn8 @ ޳76âI)C(2  P`Z[,(Lg<8fA @ p_O㪷ޚ{$hn g @~K:7zf=~,n?IJ$D%UM`YDz~Ͻ>t @ @} }cdt|s&ve,r)& @ѱ8y]HZeI: PB殺pӤLձ+\ڊ<_ NWS @ @Knd8Jvhŕ` @<ݱny=lޱ0HJ)mRn  0˶^>)ӟ &pK +6c @ @xd?ra%fud]n @ P7}iyU7v,jh j$@@jUYX/F`[/xHRH!@ @ZL3_Q\]$&s#@ @ ٳ/ZWߠ5BFZC@{k*  PW/9%"B?>1V._" @ @Z@g_v @ @A#cn6\^x @@K x[@5$J錧q /. @ @jiqǝ{sYͮYRsL @<cqy;=xVǟLV%@ hp/ʟS,0ulɇO)pĜ '@ @>rc]Φf @  +zN3~$bß;k4&D% @` d)\R!pLS @ @>ߏ׿당!$1L @ @a ,]a7UӖd4K @v&Ɔ/d#x3O^_!L @;vpK4W=I\h @ @gsQLo@ rF#>4w[k?UC DNoop=Bkc# @ @ų_k@۪kdo940 @@{/J XE /24fU5 @|lA -dfvTY=Nф!@ @Uo5.zJtf @!022gG(1(}&|ےs.^%&@&d4D`ˮ[к \y)h^O D @h|7.DLG3 @uط{(t1P 6^q$@&ޤ,S`izSx3XXlvI5u# b6Ve5 @0="siS Vğ=id  @ @@s ~#u7r|~%z4L3 @ سw_vNWƓg1>bH ܛ{UG#h;0#"]xDALn?X5'@ @4d_O.k$\ g @H=]mSeK ӖnÚkj @ Q[v.=?,R`ɼsO"@ @^`xK?GrqbUrL @)k{xUHkqe./O?l @ hԞ]yЦx=:xU @ @-W},w*"NX5` @LC1k:NL~ jފmW/&!!,J9LIΏ~4nY @ @@9{?5ݝY @^ M8aYGl.P@x+0:0~V ˞!۪OT @ @|+LbOs{n8 @ 0CCqŠi҇H|z Њ[qLuɲHƒ 4 <̥q K9  @ @#02:ñX۳]RJ.4  @ P%hrA~HvSƝhyʶS-PiK/q{VJ?{⃋< @ @vg\'' @ @|{3/8Yt]TɊ%@I hpAh .>6;Qm9űG-(g&@ @L?x{{偾$f:/7  @(@{motw(#@|waNO @^^4 8tvTx|q  @ @wܹ7EK#湄Oh @O`dt46]dk߾ @ pΣχ=X<J @ P7wϕT5rzI%r3 @EػU5!WĖ]l @_ hp5 Z/2 4X}蚂f'- @ @@^?NlJtf @T >}f'_ ; @W%_@mYt-LPw)3yJ @ P˝ʂ$:"7  @(pYvzI;Ž'@o hpm ? @E*iEYt_97qUA @ @XƶW|$jNm_e\h @ @DKzkVQ-KV-?ɌR;Sm-P`p˥O*`jRqrUy @ @LBD|Ó! WUߠ @M&g8c&I .>F.VHτ;}0N?qYA @ @X7ݷ|;wRU)~L w'pskfiQHӠԀ QmpK1$3ɌD3IĨ1I} `(eQf꽫s1FN]ν~U9; @ @5Y?c1'4hEXy5+ )bq36 @ @8o'o(xyJ̵a!@ @@ V K8vus  @a Fk`T-Yd}a+7GoOW @ @$_>R^[8IJ q @%ؽgo<협f6)&Z @@׍@(W/Kŏ>q)˙  @ @w`2ϟ#6,X$ @(Hb.uo[O` wrjPS`,bF'|sx3? @ @# {߽#7:Ս*# @-w8yF(v˺mX5.$@@1}q$qu1Օǯ[  @ @ϷV.Hbd, @tW@WE'!恢'.N7fy˦NwF&$ @ @@q{p_w>i/$xOMk @KCqq#JM8`SǶZ3 @`vOo3_0۽An߶)V.vg%\z) @ @9ărdo\Db\n @ @w2&9'o]6 V 0Kԝm!p,N_˟w|# @ @87ޙ;U +1Wu{n8 @ PBɩxF^E}ծ8> @@P(3^7?I7161E岓b! @ @O"pǏ& OrOIbt'r @/wbWX4}_$yӊW-J8 @ (po  Pjwώ,B# m>1|C @ S4=4ʞ~lX44!@ @G '_L`qeh+ p @6Ր(g]5irmQc~Ă/,~ @ @?xn/G?=1ͯ.D4kF @%0kW_13,@e_E /F$@@aMmHG @M`H\u}  @ @ |{;>Ǟw X:L& @ @uE@eED  852Z*̷iƖa'+NAok@. @ @Ǝ8xh*COWĆ}BӘ @ kb"NZ3NC Im׻P"9,D]<eǻ @ w|+ɷ~I%jE @ 0[ҨdԎB ;22A @@]ׅ (<ibE%cnO @ @Q7zOo~ԙ}d8  @M`qƑ5sUIhy  ` 6<Z!sybnsY`Hi/9ߒOMk @#p8<]m@vqo $w}m4C`k،̑O̓ƙIk @ @@I/ďߓ+ڞ,D4&@ @Swh@ Wn}  TM6-ؽ3͗Ii t#@ @:X>/>\`-t @ @SSC ֜BP,i @cgmzD~8ԅ @ >7Nr/ <  @X`]lA@4~v׭m &@& dp @af;ˁg9 @ @xӻ>Mȕ\%۴}JnyiL @f%ifйa]ie-  tM'7!/0o$^\8[KY'n\>a'@ @tƧrWV/t @ @=+F͙8@ĕ^#Z!$@@7eCڳήf; @ @wůr52'  @EIMX# @@+Bݜ8$n i4 @ @:C\_?{ʕPWDc25riL @uص+V.Q^w x3^7>CZ)Np+M:LVwda(CQ{_tbG4 @ @3ћ߼'w2W;w7 @ @@8a:k8 :73f-Ñ$@/>>V @hkߔ;I,NrӁ @4BژHڥy`N-P0=f#p`NrUD:21@ow%^?  @ @m,PMxo~&ʕEv%/8# @ `.} 3XسgW7(;Ya @ MfO \'Ɗ%Í  @ @6%rGfq%zswӁ @4T`׮X. EIVW3 @@~ D@ ZY3Sz%>a @ Ў/)w&t8O @ x4{g4خlF=u"@B(p/2yT#ޒرXp @ Foygc\weO0/# @ T]b27}I m[4kFpg` "LG`[utjؗ<N @he=-Aزb8'  @ 4#@*<3Eزy @ @=wOյoXD @]ql.EYK$  A ;h1B@g n>Dzygg~e~ @ /~/cvpw @ @vX]C츓hՖktv#@@g (p -pm*J8 @ @<4q ~}_=ވ <  @J1+^Eq0jtq D'qEXyI|bKDm=~ @ @?cC~?oXZۧ! @W`qu$}g]5RDOPDg P8CiuYP^[cb肂E% @ @@k>q-WݞN @(@mƇ [ɃF@{,@ (Mf5]۽.8ቋ @ T{~sXУ|jZ @ @@V/*z/m#^oR h7wmK@^x,K b‚G)< @ @@s~7w=نؼ= @ PllWŽ4>Z}KH 8- &D(XL{Tz @ @na|ユcH#sTӁ @B;s$+fd"@G (p  P0ѳ,,҇sEw@ @p2[4{~  Pѭǒ4 _^̵q̚< @ @[?vX2Os@ @m'`b.YFtzbF'* (ps@ $Yh] aꅛJ  @ @?84Y731U 8z]k{4LS @hs^]%L70.! @ GXU !]|8nRH @ P?j|率rc[Rn[:vӁ @W`bbO7>Ծ thK_  ~ %@@' T\嵠sk^s   @ @u{>c-LbQ @ @eXث C+*`\B"@L@O {WQхgM^ @ @i/gcr +{nG4&@ @صk"-|:%$NG$w;i5B@Gs#ML%voՔ  @ 0S?o׾}_Vn: @ @@l\9tN&FuN:2!@@(p "TIcx+č;& @ @f"p߃7>pc}I,i @X`"}tќ 3$732Q @ ˽'@`c[[tA*}8W @ @\ ~Is@ @'IoYGn%"[@{_M nB@q+ P @ @WwsO\۹}p: @ @@G udnT%MkNNPމ*'R`iۇ#+2u/> @ @MHӣ}tU =x  @ @i)kʍP[,?  PZwK'@hZqP*s<'r'@ @Ļ?wXݞBvӁ @:[1ol*je{-bt[˝2#@m$N!!^zRtuUY# @ pxpo>#?7EZD @)055n.g:I,:6, r +z˖ gYhk ^)]<[N]S%M @8xh'Ns%k_iaiD @:;J{'4JR-d (@ĎVָtI]M @?m[bJ{n: @ @@y;R$Ӭ~ض+dI@g (pm 0#s ҄84'sƺ+Q @ @} ~7 @ @'_ֽsX9ݹ@ @P'GwwR!_#W5b`c @?]k>g,n͸ @ @vسPܡDzn8 @ @,?,5c۶l@x'8A zl\d)A @4Q7q{sϸvq%*s@ @f+h:!{=C'p* @Sk<lr8~ / @ @?7nmӁ @A`bbOlH@%5q @?+MV#yլ2@^Mu@ @ @vw1M-k; @ @ _Ӻ|8\n: @ @f LLLĒy}͞|O.a]!@(p>xГ\v/ÃMt @ @|ܓ/Hb\t @ @-H4f3Qɮ9O3P>37 p8)`mEd^p"" @ @@S~}1&dwd; @ @8Oj;>+qeǧ)A4Q]&bXyS O,''` @ @f!7=>at~=@ @Pdqqdǝ-B@,t%@H_}Z=`v @ @@_r16cp: @ @誽5iĕ׻R NXE9 rm΂ٖ"GNٰ N9n  @ @@)~_ݝ;5*كt @ @ oxʺZ kFZXApPA`%Ys"HR@ @tv͹!Or@ @ $d@%J @> h.fQ}e"" @ @@~|9=ϚEs@ @ &01;֏,ū^;R'@@׍@U`9;r?-/9!-ZX!@ @]v_~ǭ>ԯ=7 @ @0҆?N]Z%N: (p#(@e*/J%?s] @ @oj9W-h  @ @'vy܋DiT^YXAvpWO^`۶HD5!/A @x[~;M @ @i 42:v֎ʘ  PO4wyY+JxA~ɳ642a @ @'0UM?rrt @ @HmJ^m,Nv/ h@+[jg4Nذ  @ @>omw>;񬸽Sn: @ @ 8x0N0R0_/_$,S_~݂4’[4(@ @ @l?MX>b'p: @ @6X87bqE`%q @㪉bU/-F0bp_~xdO @Bw}s]n: @ @vmp;>$㓔 (&@ϰmһǜ6d @ @@ ={3ObѠp: @ @68v"p[޸3*'@Sƶ\sbNͯzk-d @ @r Lvh: @ @68oO uarҕL^ёIMPdS yIRjό.8ce]О @ 0Mo@|쓷MOvn @ @:_`rr2am ӈz5m^$@X,z6ؼ@_"ė=R)I @ @7T5+HՋ<A) @h{vOQ5[vn-D$ @@ f &\Z/p (>,_87~ @ @@G |;_;wGM @ @Ǯj :+Ryege$4G@{sB@ TөE㣷 @ jƯrݝ_@n8 @ @@^0˘ū^;0m"v,0 (gXDRbJ٧iq'@ @4V#0$+t @ @:@`DL:"tҎDh&b</˲hKґD" @ @@cw|+X>/O @ @3NY;t@iT^iHMPTn ITl:&>kC" @ @7ǽ=ܥ#[G4r @t@? (2㤂# X&A Pg85"TXÚ僱yhO @t@}'hHu{n8 @ @@  N^;aYq:]]xN@7܌@5Y])z 0 @ @xLJ{Ox"t @ @*l},m/_#\@{HxC`+KMTVA @ @@G ?oq`s<  @P]&bŢ9]ۥ` .j @E [oZK`*_EزyU @ @)ޏ~=jEyUvҞ @(޽5vq/AFEE ("h3: [#CsH @ @:N`COObp: @ @1:B1ck[1B'xk"" s, UX?cm @ @b^=7 @ @#{vnEH"!Q@{WEL^ ZN=vQn) @ @@ }ǿ;9I̛=7 @ @#j5nF;*z뵫$=nY{N @ @Y/w SN @ @퉾^sTh6_@{H4Q`2ҟmtz S}N @ @W?}ƨije Mc @ @M)뇎& $qilM6 !x{( h@5k*;sd895< @ @=uW&0:z-MDhmHB$@9c[91">8iZ  @ @u|9rnI}B$@ @bbD,[O=  Pw !@IR1?bbʅ( @ @%9-I;w7 @ @8ai`4d+ڠ̆'@}Z $iexL6 @ @ܘ;J{nӁ @݄pX =Apg0K!Z)k_g< @ Qvit~=]@ @biװInhZ. (Z{{ps箏99zhJ @(@5MJ  @ [`tWFkH^;!Cm&x-p Wdolļڼ]'@ @Zc-sCcˊkE @ @@#ݍG蝌%Go:__c p{{5Ytf.7X`8  @ @ LNV)=٦ie[28 @ @MVq&d GmJ ,E&Ph-\ok.<.Zj @ @f |w3{ *6In6 @ @Y:B^]Gnh!@@ $lו7uX@e0J3 @ @HNƻ>!elp: @ @سgo[>81t@qi]F2X@{/ @d ~$#F༧B_ @ @@o{؛;t @ @f/~y1B0!  zOlIَ.}b @ @@Ajc7玦{at @ @"p`ߞX\iV#L6P (|f.0vu HΟzC`n_Wvz e  @ @@!ooKmD}{n7 @ @:4`F@Zu&@ V/:|=!C} @ @ 88~K8j/{{n7 @ @ ̟[634"+p^}"@(f*Pl=g`F"@ @XCVQeWޞM @ @&bقj<3gQtNXE9 [`tu8ai0 @ @@MV~s{ۗڽ= @ @@cNX5ИO d; PN\wY(@%M^!? yqQ5YW @ @@>nls@ @4N`r_th4GNtWLf(!@`iLvd<ޘJ @hd{{>ܳvE,Cn8 @ @  6j{Ιvk  A ;h1Bνn4ۼ֪Q/8sU,_<ܨK @h;>M]( @ @*r4 x|@@+BݜVZ&A @ @:B`r3{? @ @&giia>Ez ц+'df'}v~z Ўiv bO۔m @ @tGw{&rg2:t @ @"PV5CM$GX4>p[Hʞ\T) Kx품d˓px;S @ Pjg_=Gs@ @G{0"&?Z:{㱘siDWSH󏿮ӘG.ɂd%Y!@==/-^N[LO @>y[|r66]RvӁ @~,f{]b}G_D6ȣ#?.zOb?$(r}91'}gpL Oűm,- 06bP돓]ޑI @(@5&?Zke& @ @`f1  k_vfQ]F<7h#b7ğ/XilZ5Y{i{qgg[  4M6g۝8>?&zj @ @_~/w?;voύ @J(P}i<Odvmoű/+xw0{w8;͟$dbU7g%At\ %Y [ @Y@Y!@SfAxaOWSW8 @ @f/P+*xG>{I# @ @&"~p7U7߿[U~umӸո_{]){_23kҋ#WVk&Xf#s6z V+kdtP`pvi #05 @ @>|(`+%Qdn7 @ լ6l[bOfG_Vx(Õ{1s{Sȝv㋝ x=)#(+.JF  @ px}|G r @x8ެl7Zx&#z0>bpVWuZ/ƒz'΀͛El Ж-M8d}>8/\۟:h8 @ @@Ƿx0voM @:N`7F 8q1<CC14'>о㞉Gqgݿn wNL$Ʋ{^h_}ı+;&7<{AGt^_ i\6oSY4 @ @!?<ͯ1 @ niVzi_ԅ#M=5 BGyzk߹/{߾7n,Z;H㞇bբ#=GZܓ%1w}'@4 K%P^y |^y˞ @ 0 7y#Itw= @@Ӹj?4LƖ [1iGP=cx_oWlǶgqm;7nʊ?c|du{񃇒X89u zF{;əahv4. ܏2/PEСfM 8`x @ @@wom7 @!p(+aV}2z6ҡx9㢭bؼ 5s)_;ҝ->qYsu9-w1 N6\A/vӁ @&Pۥ[{m3=FgS[Z]gOv摏&|GoƏ~䨟x }ai%j/ w/L(j]eS~ʿ C+}R Ж^?o;(藞  @ PVwIvo/O  @(lg򻲢흻g^ޛ2OdǢ}яyC}qs}{&)>L+&؟E+F={v@_W90˷&(F:mEW]ߨjیN@qLDӡgg*QʅKRN+\\"@ @W}<]il$$K @xpow?C睹&zg:L\:g+_)߻!?Ii].i$۲)?>m (| L+I.N\KWEgZ  @ @xGodo>6[w^ @IVP}Vؾ'۹}&E Ń3}N\(?^ǯKq>b#E-ļҎ ̛S-לpg~2$+G).kؼ{xa)-p  @ ٻ092o49+gYV,dI68 ,\k6 ]…] ]m06`L0 ˲rhf4#MUߩ1jzҿg:w3zkr'uȓێM~YgԖ  ?"  0+N=}^[r톙 /]&VM>'el&=' ToO>@@@C*wo.Ul_w^ryҾEly*.j}HO [NݔqLh^D Bd),i+Ǟr!0?2E@p/ 2mxU|"M[gKiq@@@'OtIU&@@@Cf]}mQh,Gtޚ(P;۶ w,ۤ/G53E \7ޘ#&\l/ScC@@@,p\I9d  xF@W/}q8iWy /(%tNgf urߧ ?.*~T^9Ѝ=00$MՅ&G{-&A%@{$<%t W.JeiT/ht1F@@@&p{P{lN 5)vh  xAߐTbn]zUxrݿQ=|ݗN\`7Ct9cXL{p0<@ .bFaW2>Y8mW̕h>f@    !/'9@ZE@@2(`&U2_d3+k6ϕ>#.7'"{RSQ(?hyQU92f>:C}Wbi9j d@gU@Kp[h=܂e#`x@@@@ u1?|Mc8   . Dgfiq)zk++_ﵜĮN]#*ѝٖH.9M%ЀQ0D43GH AUV 4nYpODϮw/FF@@@(oapzC0NG@@\0oص[Ir~_'oz);7J/ex⹣.זiRf4@@@Ȑ@LqLmGtKj{fys߻^+ 3 M+wv)tn .qbhuEA^G VN:CW|"tѸ5+    0ٔ7WV{Jp4B@@2 ;dI'B_-\&s[+3]:xKeZYeuqqȐ[ܱ)pEwc#]tj*`hծr&@@@@_ l#|ثK4)s܌   i:uy.:f,=75m5ଌ ɝoZa9GNn ҁqYZ gދ@"@{PVy r+oD< NXϬq5G@@@R{ϊӨN9@@@t @IDATVUĥdr/ %(tA?i2̲=-s%iTpau8+| K@8VZtQ{otD 02   *ps@^+4)   @tϾK22\.|zjpVr#wo;r\Z^6O FT(Wj|E`lHi#pS0t 78:C#    |Kt\8@@Hਪھ?.GN$W8_vo´AGb Y>ֲc}FJwbS-%SRPTMoiߢ{% ܽDSPOЮBsNQ@Ϊb/4G@@@/?*>かUv;   @>dZ&}h0NX %wqe'H@su^Cg uF Q=0 4\u/#y9.ΰ    @_ 24s@@HC^ywf|WJ[C's^ee‡d.~iMi[g_~[I E&@Vx@*G 89Ņyr܆Ig    @6rՒN}qykdNkel^Cn+R2:(#S }J|4FWCp5-3 / L@@@&NyfGkK5^h  8"ێrwBǯ7[Wv26zSYo>Ų=/`Ć\76W7:##S }. ת+]a%   N+)To7'X'[lF@@_ t G@@@ ICc#m@5c ծt^=     opYwp4@@@$ET.Z֔v!@UyZPꎳY qn# .!f;ot NHpw* @$"[] 䃯^X# 5e!W`    '{zIT6[co;$t@@@ n]U0O˚ `'g Ub kvݾ!^z an/ѣxFFs@@@@ ʟmsez缶Vh  %p!zˊ} 9uEGd6KC㖇9@T屹&iYŝ  Y*EwTF= @@@@~?簕H}&9{؍   ]OuQesk9f7WH[{/c0dAKq&:$ M.MTNCټ%PQxB٫o*$ L-^CHdi?m^y\Ɵ,3   (^y#[Whu܌   1ţ6ڇ/ӧ@/hl7llŁL J~y'4h}s8xFY AI40U$H<|y잾@@@@ |MBcC@@HI`d\#mGT{v.1h)U jUtΩw4&b>ټ#eAkw a$FĠhם)#Wrč@>Zi{fY27<    Gw۞`ITBٰ@@LLj7+_-+o۷( ּؼ%Pm\]CHpwuDs@5y嚾ȵIqQ@@@H^?yQFT6q:   pc '[W;"05Ջ-;ԉͫK"  N}2ۯ^$޸IY'$}Rl022*3ꋲ=,&"[No@ ٥!08%ryɦS?oͳ~Ah@@@x{ G5nn@@@Zߐ=W_4Sy 9u|c";Ydo:NBI) $ -˿@S:yόX[@@@HW~+x1 @@@ 8:,[#b$dD`[ڸu _54\"w炁x]w콾Bćsru3\@@@@~S3>Th  Z`LU~.MiE"Q^t_'Ip4eZYv4H@E_Ȑ [HA{:9j A]q:    Qx[J7'yJ@@n&b6ڿRȤ,<8:Ck@@@|#GdᓎUܼ1 @@@ c";Uʿ.C0qmګ[7 WPt6 S5dPyf28]\uaGm    o>bJԗp@@@ t1׾H\6핥Qm¼0aV̫ bx 8.¸>##@2$'9 xq+3խH    n8 4)   :35xQ]ǭnſ,-u'q ֗JS}mD=v.HԆ}řflmA#n $vT("y  ,ٽ@@@8S%n$;$t@@㺜KB*Ĵ `^d12qǰeCdT`z]4ӹkfq\ E|Fk+oM6YL ,3M*29}#   S0g.!1 @@@ t=O ~ -/ <1KD"yͭ!ʸf֌ @$G3Ȭ@Wij5.,emiy    W~rsq8ueDowF@@;aǬu5G޵&\0ֳm܇/T|%~w#$8 ]z&.. u    I |'/&y晧՗"  )\Gu䷷֗>Yrsyu?%`W} n-ˤo)NȘ@YsAzc@` <ÜM@ s's\]^ fLv@@@@WtʣӭHB 9e|@@.]F+^GssTFC&t,02a*[ڸ}w۱aHD(BfPw.!#twNM<:7l%9y   ,nYRV_9l   @q߻^Θ.fy1֞_Ph^NPy _%"D d0rٙ40M*B~?LkpidE@@@0_J2?+*NAi-   ZAU}wn;./7^2"yg҄C#*ɝ{. [KXh"VOo"4e H}T@x4[߸CpV[3.    ~O:{:N?@@@P T^GuY֒׆҇IC`Nu%ܽTpwqm"1=n#eC<%@`@@ Zq顰fQTU4:"    |'ۓ;xK,~D@@^ѡV./ʗ>pUlwFM\0"r\ghH,]؋. 4oxl5tKÇ~+ֶ@@@}g8X Hpwũ ͸8 0Dh̛^@@@@ot;1+!  )0~>MrVِJFƝ_0o.Po(o=c? . 0<!!gᇬ \M O{@@@@ <q]}y{,;V  ]Lޡxzٿ(9.V∗J8?aHyIp bg$/5MG0HpO@ K$g a@`rMTg5M~&gdB}pO@@@H73cѺ2>u G@@C+ZF Dނ K1k&yS o4 r7aXk戀O䓅"LB!C1ONrFFX    Ȏ;SL+$9v  [Đ3}~Y>6.Qyݱ&0-/kc19M/9g/;@Hpw a@\C˞lK[l     @JxŔn#PNi  :t.Y+ր[{l?.Wb~31j:IL  E#d*`~Hwz]gX@@@L`x4&{tds@(Ms`؁  ݩȸ5rĭ ٚ#,Ǘ2!(%PDʸ1 @$'+y Q NWft:X@@@@l9@@-oHrs#ٿ$lTxܧ M؆m*l7Qcj["B egxO@h\ҲD_3k\a@@@@퓟t<,~D@@ Uwv|Y<!yc X@Ҙ=sFnxspA.3$$e_. ~ͼ4#    @*s7*$/q3   0T:t$^IŁ? @euxRlcc#b.`sGQ@3Hp?Ӄ@%*dVFfX@@@@`ro?CD ۝q>  Y`.#/bUAhVYk,ny"D04rBLO e!(%аmᚵwfl^w!@@@@4*-Gw'osE*(g!  @ 9cn"FBi ay6NϨ^CO` "eJMw"@H,|b@Q4GMqadD@@@&xɃr'uFmƭ2G@@@LyCf{ d638*K;7ڢ|[`O[祯?zBR =57Z!@4 =N6%@@@@ Ў+h  ݩhzZsZ+Xm}Gmw[+Uq@  @@/{!0p0N9#   O ˣOtii&@@@W!f&~o6IahVF@eYeqC@<y(\hZbZrf'Hph5ۙᙱfxv"@@@@tboG]Wa  ^`L%`4S8/̋7J SoX_ ;]hL.av5ad ٪3]&@Vx@$?|FC5fK    ЎI8pD}VSgoʰ@@&G%]3ܾY5|C$PaS]g@A yfvvL |!@/ af %}E@@@;gwvɎv$#)[<@e[ڸC8`Q]8'ά@@@@To7'U[_\D@@ Teݪz7HuE)Cv9{\`A{Ж 7bċ@Z.QDv6.iD_4օ@@@G"E|D]   "=]Uug˖ Z-sM;ofbfwoqo  pQ WV6._^*ˋ\a@@@@kv>H-d؍  a4:׬~k   ̼>qH:~V9nG@@@;v/J X]Lϻ }` I~\Zy3.ΥB*o.Nr-xۼhۏS;Ž=26RT& OZ0x_xG;4Tɶ_7 ߯7O `ND@Hpwa5s#Z    $~/A"%|mC!@@㺨eIsmqGP^{?gbf8_JnN;82.c C 9?' &Ew xd~ h CsaIWKqQԅ@@@H,엡X6{kJy[ۆC   @1#'UVRW*\($5uI~\#_\&uswey\4oCT/wSP|E |ug"IȋlZu.3,   X.#kJ^   4=]UߵF 9 =,]ny_Hx~l}r2C@<ˬFVR5ǹ%|E $qՙ3. DD#%ų]a@@@@sz=}))Ф0o8  H{C+e͘-}_\q};GcrXv~˩Ϲ*! @D̿z8V${r(nyRDd6ydz\J=T'cnm:NXWp7/d@<ŲЀ'ٍU}e_-"_֪w>|GO    4ۭ5OT>gCH,0 Q_*g`T#Wm3׬߯1x5X3A4I @ xZ CcV߼uRcǏ-u>{+&diiļΩ|X#;t9>`O5 Կmͻ _*_%~Fc'f^Do[k}s۪|<BG@@@ ]JNq4@#`( v31I#R{P63︪~|dB8ۦM]jcB7ͫ\)f6?H09buJi+Mt*[z! wd9?|w;kdzUdAfU þx»U壷趩ȳа=jǭRy!fB#n) ugdeSŜ@@@@fu8#kJ٧KN 0*.:tV aJnOg3#eo\vurB%%QC@u.9vޜj| X\G{\RfºY}Fik>S]6*lAL'GؕyFGd~tF@P=S%lJU=ӛ_U^05bپO%Q K}whDeMq3r'>PIy!OJs@,8 @^Uo#s[ 'O |z9G%۞bQ G]:=?̯L0?E7/P4s3]??Pk3<'P"uU @@@ȮX\~}Uʼn\kZخ~Ni[0}lU fRYաEIQ\{񬉯*;ˬCn)iew3 Cӱԛ @)=h yՖia@?&]m&/`rT^AIXy$ r!F4952<$u ݼ-f愑]uFC T$j,YPܳ.fFeH@@@@\~`GPއ =q~.GOBaW&tޜjnlٺMZ(d YߔDuN|?9">WlOgicٱqiФY%doQ@ D3||i9Azѳ۶=O_/'G2VE<{4uº{r_߳'0>ֺ"1x!~θ&C0Z?ۡ^~&@f"b4fvzO$lnMC@@@.p/v4fMi$v4B-`&RڏjunmXffj;3y &v:!_{`|24'\êznJtoTM*]eC@ f޸410@f"_Șy%Ŗ*/n2ۭ-:mޭ|~bLA|^б~)+o 4WEIpweY"w*w! =ȉpsZ+@@@@ ɣOrQqTx:A8K >?ҎT/m7^:O.]3]dۜ%ڏ+䣷}*#;k?&%ĺ{\ڪ#R_nެ @TsHj^ե0=./>!Ln_ U"}HǜYŽ![ddbOX]=k 8vR_vth^t|_ɶ( 6ö, c$~](7!&*o1   Vl؏ I3}8M1N@$m+Oe#=5 @6z ۭp sZ+=7,l%.lYMZ&v:!翞3̼P!m`ԐgܧWEĬʆ @:̻9cwx1L?1moIya/+RŢRzc;9] 3&044,%20tl!kԑXe7 0%2 GcXc"K?sΫ\   $)3$}hgzw_;伷ːIfEyrKeV10rɧGyPi$٭CU^_:&y abq﹁:=2nO>m-,S{[sc42,>#;ZFZ׳W`VCW /P1=䱸 @{) E]VZ,biFCVc2@@@|+pX<˺b*ms)e~ȒUO 9ھ~i;Vˢe3KKa O:dujPi2."y/  ~a>ذyN8Ss7bk5ThRUn&m}^XnI##QWvbޛ! ["B7l @K'zotLfq@@@ȼC}nE~Ȃqא?9w_*+Hn:er"u򁷝/IV6q1+! @Ϡ!Cc7syb_?nyr1ˠ̨v?M+&'-Ád$5|aSq|<&5)a/Ά]iKǢC@z/[¨    ){ܑO$' i vQC=n]UKBĭ:ٸD<Vz K/ Tq]d1]^v }n:lij  L~B4H| EisZ+6|@@@@ 噗Y^ݭwH@ 3}q]9ˀJrOf+Por՗_'7]6_%q[2n~;DLʒ ;c)N9 @ PfVۻ?V8݃6WjRoy8^{*~vKp묈ۉ@zohpedE@ [!JwF4-@@@\Tc Kʆ 1~ǐdk}K7]o^)%y ^=%оY7H*p,&a] '@8[Igan<&q]>ke4Wyҳr2oG`ŬDnIF\csE ]A@ zhj&@x4M3.,wAl    z_JW@ 5c}$ռK( ũ J+ fT ?52"yTNl;K<8 7S=||1/}2:f>:"|rW&k߮X?F] ;RW Oₜ@"b^_$C$p hXuչ1,c"   t'usߘ&#Y-Ce/u61yOff%MꟅc& @ do7t`Tt@\cLEc47$+d;s:)&3lW]ͲlQ5_ ZYZjDZMI2m@)PͯGޭtFu "潒6NcAw#/+9j>pZpw"1><&@"v %Xm-At   ,͝'_,\kz q* pjߗU lnʿ~.I":30qxK[ryNg1N@p1実|Y 2W\+v)Xdp&41/ju=&IQs*0) s r l@@@@&ge@MeH@}i9|.Owwr%s~-ΘO%V%so{ӪN}}ITN@Qe4Uz|7}^N'r.Ho6Z u FV4T{_:~]\@p/<"GTͫΏss*+dq7n^#Ɔ   }ZsɬIɸlR*؅4xyY8$s7ȿ~ TaF oru}h={Gܮu;@#SiL~JNܧJOsv)w)ӯM2briuۦMzA]b ,diliwMs @gLʼn 0@,qs/;{o2 /A    P*;S-9]E k})U Zk?[yс!SVʶxcw|fǮmCT"R}f6WAY |ru:0,;E+ˌskʼnQ1e*y:[>ico6*39s@L*p\ q–5%.@@@@+:SC{RMxl\:3Iw_|ܸU-(9T`V7-dAm uAcXe G@o4a~n7$C˭?1Xg o@v=ep?p|и.:>%iYɝJĎB>][f!!rmOS zK@@@@:#:=۶g;'Gt@~/eX] ;|,ki5Ёfϥd w['*@/ \O)\zR/M'cݱ{ѡ1 )J=#RS8>hoPN( Bi:NWw-3Ci Nug~)Ř@@@#pS9M#r!#*#Nv%R?77l^i۵ cӦpH˩Dp p7wܶL $6'WlN uEdVшD -  JOx @Av4̼:> 8@@@(@|.)t-[גE-k7.@Gr|Jt@]iTsozvQ@t]rǦ۾tBC*}bvm+T"Z`:.21c~.J=DB@gu.6 ܥ8O7V gG-.ꡅs]r;Kro;ƨ@k{m/.H%B]:4_k]R׋@@@pO鹬GHɸNyҶ-[͚}U 耱eF}WCgS2#) 7VYA-m:꿺oqu}GS~yk.{^0MX  *҇ʔ(@(ύ׶#   @ q2[jyN"(4D\m7,[dQGUG'(?؏ߺv0&)rlRfGf. \7godvf ˑSݧmu5=Dm^U1ӭO6בCڭظUˈ!"@ǥ @(&kF#@@@@jJ/]/>˹E"28]2~l|w7ЙQ?or') NnPz۶ޙ #߇ :;xDz;Řܟ:bp޴A9. T]5OCFTWA{w9nxqRYP^@@@ٹNfML͸ S߬hǍbw~qm3L `4Dp\`&{ڹÌ+{Ss򵇎TĘJ]uʕj24FT ;EKdp/;"?z}Guxﰦ\G@@@dF@~U ABZW\L!   @{i^#?ш|wK[^s=z}t c 5ۋD(ЄxAkܸX^N5vqjXetYSG뫼F.pz N"*Oa. Λ#~X ya>.j0#   L`x|VzVܔ8k7.@ (&r|>B=|w +  y;7țoYbKŔLٞB% C^ 5W?bRmeH[zɭXƺJѯPF'faPFIQ@c7yM҅)]. / bSKOWC zK@@@< 7n|j`@0iye."'~Tn^UNiX?؏kw-AjP:_Q` sȔ-#,pҔ|{)׳%SsjlQ _q"SPPJ)+(!<~o͙1HEiL;bE5    PxwSM~0>eEoʙ9狂 nw*Ur.*Pk0NMd5xG$3@UDˍHEZyp!V_DudF ,kKq{l6G@@@|7w}n^S*p~$-rǦ/l;:)P׶IE2H r@  n\dCw\R$ڥG7Z珍ۆnHGTF*D"k|*@Ԣ8 (@ ~m,쌽#    @ 6j7uIq)rVY._.e_W[Kx]J    d<' ӷ60~ "U xZG톅) p;n_&74ux2-C@zެuL{wlZTNi(_{踱ڐ$*4N3wQڹq DqxI,G(Et|2? L p_땱ea0@@@wwi59]E #xZ_0<k=wѬ  ˁ^HNA(𤹽Mk:DgĦ_`htFgh[ j[*j?gNXE/ ?HD9lzBglA Alju{ު(If;@@@-Tqv_p*"" 0͓ `7~8r>vf?GvDtv37 ' F@%T^Tq ϋ~kUDb> >3g5+ڛ~ L[N">~%_!055#@(-dpwTYKfc7~@EYW8!]iU+    l}6rh\SۧmT֏JT @^&mēyu W $Ծh}7.lf=t8],7ej]w߱q-VƉR@Z tyCf@HtU=f  qX_\MT?V4:#!   @}:'sb"|#pn$-=/ʭ7sf"R^m_dNLGw @#S9=ҫP/p\0MLl>];7.G2'IEc; 3ѶOL@<@ kt2wQP@@@@'r p׷mܝ/ 02CTiMmʇޱNcsU ۷/Wu/UYj*@4o؝EKZJQZ[q|aK1o߉dJv8g- MӗR3JkcM"e|=e28E^Y܍4T  :;bt̸-N,}qmuz:7F_A\K}>sɞO!e*,gDo8.@2U ׃ ܝ^WJyn@@@,@{#LZ\2蠙r @덍HC T}h|5x`)Xj+CRUadT݇/d= nNA $S"l=?A69*(%rז^cSdq7Pd$0ujm%_.g xX )>w>sf< |ÝnF@.0td]L}P` ug,^nUKW!   @}lbl8o !XFlNy[۫*IxbϏoN1^H `uSٶTq ʮ#j_t] eaybzN9vѸ^iG`zzF|5RC&𬴈p29"pmQZQ5a4 @@@{ޮ'˞\n@:9X\[ Y _o5ǏLEF?N  !~.MاӒLYEzʲE*W}_:ET5 @R=:\5q- =ΔU{˽UE^u lXޜۅ\    }9\E{Nh\GtLЉ@_"m//#@ 6h*Z8 daWK]MEy<3v2Տdl2/_3zbsaHCEi]pNZW `U&Q0 nw,άi@@@J!0HZ%j=N˴!ss˃,p9߰8%T \%^c*6v8"<׼Ɵvܗ;]{C?E?2J,rmm!j  LՕc@ JD ܻJ+]"   @PMd=O .׫˯XKA }2F,Vq~6?_nw/<,sFf;"*zʯvܗ-toz~" *_"gx5*P([\ =xkΌ] &=wj%=g<.D@@@fgPc5v>!')I7{Ī4e悀j孷.5ΡԼax @&gҢnveYUK-8.v`PSmȗ4q;cRDEt7U]2c5d$J~82r p [sf@4sjix5!    <.Pgvf xZFׯhZ+0='oXaz6^H  id7픲0~BS;|q0*ݏez )so}XW99-0==~e6)5ٜϹ lR 0{\)Nl]pt   ATQGN^zuU!Бe\YKhH]?|=65.mZ0/yrIxwɸޕlzʗ/jpAwݲN^G*樗ɱ3O.FD|P"xEܝ^5׸M   xJ7L=54_#TC)'ͳ[V*@IDAT%A eq9(|pJ@??oYP'o=vxucMHm4V{bj'Fzў@!ch6X霍A =ȫB`>#=hS3Xn FA@@@xdٜ9qP1F15+?SsbM7/W y.;qЬ/ p~4ĦRVj/7[oRޓgCko]ebFRW8hH /iPIT n{7oZF?ȡ!.A Be;gV΁1*@@@#*{E\\ N ?R[Uofk*dY,@ N؋G6Ty?[>7":#U˧3LͿߗ/tZ̫e柑R'(}&ibЂ<p@Yxy`ꒉ3v] @@@_ =uI.DnfC 0Jˈc*oX(oܺTqpݷ,1F @W Lz+gWCwfzꑲ,rg,~=&]AAPᇤPЎ9'R j_-#@:&Rx7Օ뀖@@@@ P{ZFpT`9q˺΀xk3i'gSWw+KXR;v78~ u}k`Yqu~YI@yMi f-X8   x9XLw9p>P7v3{UrkfTޘ}cqƒq@@ ٌo](N~O;1k|;3=5,֑e*­3qW}0?Ρ{˽M.B `(U ]!{{ %   Mv&mo"q.ۓ67JyΌ?oeqCiQS) @gҢܭJ[S,Y`U1 \#.G{Z}'&ݘ۝ߝg HID "{&N@1@FAƨ4UAZB@@@,GEgH˶ECQ@3h.5m7n]쿉3#$pMr .ED|(0:muښy\SjT& _>4Lef }LÖ6T'0=3^!W~9\s`&sV"@:&=|.ٸ5˹@@@H*4~{FxOtv3V N@#06m޺+0^#OXmEf P?o|͌i%Z~U E+ܭT8CB'VN@@@/`_,Pwf"`;7M;;AF/ u ^1bs@Ր]Mkx=u{8XuX燊,/sg2o'eQ"*۾κOAXmb5Mf2mA CPo pW'vAZC@@@,}QCrm/함E}6á|=7zsb^#&Zlw'4@gSiI'T] ֕uCri:;W=ČyӪ[3|fC5?8HmT8-@p1dG3jE@@@(SdT}LwȚ @\eo0fyl!wb3 RHqIA*`z6Q(.xistzOZ" X],pgQ(@yCSNi[lpeW) wm5FW    D۵yVvs? Ij*:#ZoU1޲8ۢMW{m:{ny&Z]AcF}Ȁqbdp7PQt*Yh% tC-dq"@p3eH%gu>'-VuyC    #O𷻎w;.!]M-do2js yae?&YLLϩ` =}fFRV~SbQ*T:ƪ !P@ٙYWn(]T8Y) Qw#  4?tKU^@@@$Hl7Y,¤q+p&{/kw'@(e9 4  t,eᐬ[aE*c\ؾsAHCE S@Ҍ IhiF'rV.=G Ct uu˹O,i@@@?7(S3 J F*)P?lq"Yɛc `+p͂Ī,6`-/  ׫7KuTEHS\->kB:?TY]X6,sv^3?<2ƨ_yKX(%@K$JvQ]   L@g˥  j*ݐ zyTؖ,y4Υ Gl6l\Yk1|,=7߁FZ72>}.53N 8z8Etxfz1Zwv: @@@( S|vDp7[6,%g.KФēKʬϥ..*Twmu iwo<FӲ,m8ܳqE'Snj{Čq~;?~_R:E*$BC$Op1\ w; @@x]?PO(j4@@@(@RE>uB/e GxZI|Oݬ㽚Tn@tRtP{p&oiF"aUI[V.n]-cj c*]-8@:v f.j?gl.~;wθ&do7PQHؼѤHKHֻ\PB K eQ@[Smm    `8p|P&Ldo7Pg΍CqUlZٹ1p7#{ϊ.DJ\su ٠n-6.1 _,h*8ڍ.3quF3 &iIzκxx9Q{5P6scan\ )Ly{?v{_>GFL rHI7&9GBmok&Qt:C@@@ `8ӌɜ!Z1trF_G@kWz`dJ=7䑧kG)TA̗ݰPn^%U WT5״:c9YUAwK"_9n\k4No3X-m1є츾SHCEfg㗟IZ\&IY p1 QW#0z{;)TX`5M)} lFs    \ܫ "g#}6um7.$09'N~X.W?rOu4"oyVZZѯcZh prC$Me6S]"[3'u~OkyUMGam^en1@"Xm uN:}}xGw#Equ6oGK9j@@@@ wJCp TOY_ R9wNLq۷?\I7L$߿?:g߰r{ hJZ+z}O]F *@Wv+}5W?NgӊU:5N_/w_v=ڝjĪL5Gxrz ΃Tv#OQ*v]E@@@CƌvvBN:'?mףjG˻~t&?°Wp\fIAC>']w,_~im$酵 p78=NC)X'J m?؎p{٠a;@@d* X39^:ZB) @RgeK"ePG.|4t:}!   @nmGAuxK@~P[iy}ɗ̤,|w9iXŀ?:өǐPssoD2-n(s}?Z>륾MFKL-x2!8k_̚%RYQ U}]4vw X}&ckW!,i8XdzCDqT@%Qq:C[{k- {0u   Qsji9qH]ܽʕ⠦U}SgG&ЁBuU!Օ*]}K#Y){rV=VoKsɿD>AWUuf.\SQ^&KTsG,/k\3@v7l}1Ϝ5ޡJ 7no^ w2 @$d j:]04+J.'B%_;Z}Vtp6^wrlTmM @@@@:k_q,P"Wgr& ΍3iuZizۏO.978\Z?z^Ȣ+I_TAS"&Ӣ3u֣STw?E~ݒh!V4nPe@7 ͘Gw6s%5xxoq˸ze*+{\yjl8@qRݢ[5]~C:跉1_ i_ T*{RSR.XM;@@@D f[-xb{t1ll*?զ*;$p¸|o=y&uK̮`.^Rk*ECPIG28!3-}'s*-:<(+zZ7=;$U( uib j#]z{8FefƆ孮^_3- ,033{ycm6Y_ Q?%- ;}Lɞ7)Lsk[@    @n<ӅER``T-wm)SJ!K}|?xgB >gY[jCi8M\<)8/*/̌V쮦22S@? L)ˤΖR(8~fDzRqAxO;nAÍ'Ύ%uT%݂C$IiD80(;wE\9tJu4"S3}:1f@r xR`lZ=ƕEaFuwY{Z%ǪtǬJzl~s;K4tk=&ˣ+Ntw  S|͜9$ݛiR@FS    G_$#/_|!!1su]RYczѿ=pTn}YW GKr2ivp+ϯYa0qy˯~]ԙyN6s";d|2Ѩ4w2P]29IoZ0,+:â<_?Aћ( t4# p~X i5-VUsܭ~=QSv4/w!ހ_jfVWG<KNxA{jxa#%IX   H.tC;:][hEDpb*X y=(N fr hi K$@)ZjCPU&'.dp{l̗ f(JDɥ&3W.m Im4>w ~6-'R2g%2_&e~Mql  -#O_']̝Y{}^7wM& AY3 y̬kVHfZS-VizX'F5=I1qt$lŢН2Yqgf:Hʉt^&2et("  xL3zjdoZ#]uyd;/<0ro~ E߼/NE' mKGLe|`~1)DZ&NHEL\H 0[Sh2''Geθ\{qEkDj^4Rԫl~/KӝUsmoZ&~*lP J2tY\gS*@LE]T_@CC@@ $- N:#pqaoKJ^;Hg˝\F#o ְ~RWw1?%1T|7JHRx,CA&5nHwW/.^rd&Ec3e{=̱wݱZ3Fňc;ee'N. (z g{;ݵs[m(d%@@pV`W$N@ /mF":e8g/N?8>8o:ڎĪ<τ?FT@6GWuD*޿uA]x{?nY%Ɓn+VNllau ǜx` doד6t1BP7>uӀwjΊp48!@@@O2o~D[VWhi8^Jۖye*瞣WF*KEeg !Y ,yǯ [2&t/^ Ƨ#&{-5m%Xz&UnYƱs9͞/1TUcҡt|@ 7|'NEmk4gEqz, I" Jk   sgFdp&j0a|#pi|QZ웹e"O ?ߒљyVu RIry>A?QA|IٹDuu@sCTLw%;، +5uNI!lBDoh*.MW1<5Tp7pv9!ܽJTn^ |w-1 (鋈"t&S2    5'lnn7tC;C3xsQCAs y< HٗeyGXti6:{*h20.NTõ6VY6?MciAMk2{]iw$s 11e-Ru.J4ax17|F BH ,PC4X3<%@{֛"@o'r^ap/$\-@57  8/G}Kr x;7i3.ηV/N0t+Fd&]WuoЙ?ɜ!*׾|]{ yhEDV6C3zFm7tjY[hnN"@hx@w,CDIm__v8g Aedp/,DA@@&`wo\t6J/M>G+Si1}R#kiT\? OROYg}qEe]2^omF~ȿPSi)i@ Ϙ^)lX*.ŽGȦ5 رq ˆCEIfg)q0BOatC<)} %jE RnIAdp/hR߃  xH،:?kUF\@(Є;Tv~ ,N O2>m.$2b{9茟KZ?coG6UЭo/V J*e0 A+<8 Y礲;ͩL^~$h7rWĬ<>d|ԦE nH'zO7Gc)_x󠻞|T=^FposaN+zb9]E'@Og#  @܌.%_@:~sY<1$?ot)h'4  P@RUw x[@$ keNoO?|:]iSK:ΨPuKR]avܨW;߶0#o!8xr a9n~P>o^]@Hfy^wθfdo7PQbjv^8tz}[Oܽ>Gw\K %{a=i @@/腜: sJyYa5'22>Op(L󬚏vꌭ+;â6/r¸J ZYft HCxL`*V٤R#U֕u*4TشI@dsejv;k9V53PZ>s|mҡ!gt P>8-ӪITq(#{^~W   f}}bJNe5J#04iRùs*qyg+ڪ ŧl<)DgX T3U wn {nni{. epބD|i/OsGޠlJ0PZrO׎ (Ck ^ #m]?s^5KRyC@@ #p%˺d HOX8R[vBJ+7}Oώڞۢۗ֨ !ɶ'ȣϘsl^h ^X%ƈ L̘w:_֚o\_D 9{q²;N+z$VWiS;3v][AF*J+vw:itC| va&Lwa)`dp/% !  {Kr"<$0:e5R_R\SryF-!VќS1+5V)'7q2Ho p>&e1   y,S_qv` wt6Dz5rYGFRksLLϪTicVtT&7wgDq[ ķ;t1nY L8{Z1ͬKƤ%4;9g\:^4P $8Ø =8LWy8ҡ3]U玁0 _ J(躒4  EstЮۺ]-u i(oZ!KX@'<ǵT:J^n)<9:X>`2Eא#]k6ەCUw# B]fzme~sbFx\"Pv3SH=vHg j>t08dpw\:#I ]m)'!  @1ok͟: 0jqZdu>y`)ˮi ŝzAM9n{)}T3f2n@ gpo)\GTp'۬wWr8кy^cgpo5t/@mW~eᔐ$ =x_-Roo.v`0 dp/('!  @t\=8-0:eμu^.3g叿0;c!i TwSG1^x8=ۍ4T GM*&KdpwB>dpokdJdb ϝ[L|7FwE-n[t(11(ҍ@Da;2v p/'!  @qݧ=w1++rn\&fӒPA/V\V/i★̜uTUՒf3x\rΓnn2=K{ @%zi,Z4ޔv!c#˸ n7k'6nyIv'TUaMk:yoKNr  S./ Y&7›ƨ(@:=j#k٪ wKH 7lL2삂  E` 9˭sb"\%06exٺ&.ޣo4?r[+DeD`Ec9K舊vugU!ȴZsZE&TlSYTqYRHB (@ 6a<B|ېk/%kM8  أ3s)\p9`k:w̩,޿GŐKZ¦ռo}Lz7&(g,|U`.M Wﳺj{]U2t|UWee~%O . 7~?HpM[8 $qo    @=&X=eǧ!`XRlkZݵǺ?ձs" xA_^Z:>,߻W\dpʲT <A{pQ[K䛹K7a+ϥ(@i Mk; a#-_Gz9@߭/H'w$@@%06UT.O\}^gNf-ޣ&}5UIO.ncnڹG(=|Q|-{h4ߜR**RyR]]UI;^K&ejAu/዇,Q$S!ܑMC78+H[Ϭ \ {ۂoc'♱ }"@@hWČn Y[)e E$sauj\!XZ?\E-:K+z}$Klїj'~U[(PVHDU,BV k%[dRp_ !V)@*L:SŮl  4\q9C|VJAKszsRL8M.͐'g,61Kc[? >[8^ruF:7B=_3UTogV1jzEE X.T OF v$i pWww熞!@1*Yh rvZ&@R%@IDAT@@>'Fڱ=C6ֺ sRnotJ웈yeKL:N@ EwW_'O)<%g'c )!9+Xg"DaЏ ` R Aj pvɡk'y^ 8psS!h*)Ś83ac@@@`iyE;?YsKVoy7v@tRKU.」ۡ޺ӕ?pAІj%W {-4ܥͤ JlXn K}4]ڦ |Kb ل'ļH>MraJo[$]}B;ϒa|4@|9.0ѱ^])Gc˅ $?KjB@@WڨqɓF16G=ŒR&ku33ȬjIC)ʅ1(<6'>D @q[lVm<|+b mя{٢pe}`PiFwtv黉r ֌&xöFއzB.O GJn^`Co5[v%~E@@g/y-,l5Ͻ>^}XF-HAhҖy4˥Q982;ܻtp.tDagXyX.5ޭe\Ҟ!2Ϧ&; \wߘ>RH+oҚ ԏVdGw@@xp[k[)e %6˜}M}Z_5Dv'*i9өx_*9Ɓ@lI:siڳ{ 'vLԋ/xΗY)Oc"Q!=xvZD9ܝ:@"{콝ـ[ ݿ'?KjB@@כ=C3Am/P${MOrV:.? wh 2OZ6]XV.ˁax r$|u!]t]ܯ K=7;5csاN26C"#_/˕l6z @E=i =`6GSOԄ   Jeux*pn쀀k:U\n?w7n"쏶qeN\kBl, @%U ][{6w%K+ӝmMVb^^kێ)@!cԺB(A>.a^DJ = [wШ)Cϑre/R#  Un_6Q5.y()$ضN7W<5&Ygo'Rqp?rZa]ǂQ`֒έ*)׏;ttԗ9y߿.j(@ 4pIhF[lݺ'|HidM@.k#yo2Lu |Iwndp   u LE!#yຼSmvgG.ض do}V` :5V= W#"[r߹N.܁cW>"}^uM3 \EqDFT*Ef,aNA{X&~"@KFB :y"_Z=#S   - 9p9eCKa/;T!əEϟԓT<Q\A2ٓHI{@o2g޽-!aepW_X~?7fl7&f[Mb6 +g[g/O):F{_Ҳ2g5-!.}C (ZTSQZFdbG 8R   +p dbw f䠗m:%ggeGʛgΡ9;y XZITL͹#wF @mExwwnЈع8doۥWuF_8pQB6a(˪=S# #0  aVAG F8FcSG8:J/#!@w/   . 'M=;^sL;;8'hyB ۬U)b} Eh>f9UFu*.y8-@IVNZwmHr 21'_|e~ޙ|Eɷ ?!!Fh w4\`ϙxq4?/@pY˧k@@@jTKmu3\`P@Xl27/Ób;:yOq"ZSN-+"f[p\mׂBlg} ]Z7xPp|5F"Phπ׫~i7u @:dooPPj ^W@@@O'F.wu!X.Ɂ/}ِqo+7'>Q.BuJpg"  TVK8>_AjC@@?ஃX@ Kwl\1\*{NvR#&+ fRҨËz\q @frҢv }&u!EvMK0:&45ֱsR% pÍ]QMƂ@U{nk٥+߼ľ +*_PjC@@VN׼I5. ,udpWW8*Nݺ9;R <Ҋ D07.kδ{6wy䛮:tF(/xjA>ۭ߳% t" @*!}r@!tyB{."HKA ${ԙҜ,2X| 7J*B@@5 8?KBĆnZp(B d˶ m!}eurxJxf"N r:p[ZOJE\miDr1 ߳WWAіwn z=^EM[oTc̟X;OkpcƴFF PH @p3zD_홀[8 |9".@@XuM{]l섀sEIՑ!hK7sV7l )&kMdqZ?,+';;Q8 `˂Nb".0f=Lt[gQdVN z&{tjԐ؟|B9] P@yry]m[w{Fz*@B'07=i.$kcs@@hzy\zTf29qN.Ħ wDl]Qg.M%]^;нr!%M?4"'lEf93o/ތy ,-/7j]R}%k@ Ni+^$^. ZˤT6cyiڥ ܗ !   qRlۙGt(C <%9e)urxJ^Q%m g.N􈄈}u9{ $4rY@ EXꜾ~jWQ͚^KRn7z5m}rlxiC XZ*駥nwdpva-Dl"-P>6B`sR'@ M=2>   ೀSc5ךI+Ւy7v@,OF#]go?/Κ mPK9mjJY,g5'AzwN,pZ`zAnߵKuwd;gi[wWyw^KqR_~R(&Pr _"B>2w ɡg"QE 428= 7MisG?dpϒ@@@~ÓjXdAh[q.dŲ<9q=81+Y8?y r{+ 0@*` p0۝9q&(/{tjݳjrfQBJ8ʷb B;AOhMaQ\Htwsq)@nI2:(WO*C@@k|u 2NO>3^~8kb1YbS W˞W;8na%/0 l )iJJ4զ?n2'm77unnm|ᄀ[WU?= tv&$. D%f}N.b[E{ĦI'm@@@@z)ET*o~A; 7u螶<മ\BpF!`g;ЈfGGZ n: Ogw;9 Gf']|S \Pˀg;h]ױ ;*Ϛ[ 9|eA H}&T  u qr=#P ;!R9nKe3NWΉj2J pC"Չ.-:[lXn +Jpp}^mQ.!9#e',}`P%OZt ۮ/ZS'eYe=wm "(@{'!!P]z[a噧>O* [+Y0>#  @hBsIu2M͛ˣ{ }1&ȝ8x7k"{i8p`D_ڎk9ҥu5$(|'T  5 'OָN.68-` Osp`8h iy5:&!8AX@ -=o'yd>9J%ϦYG?I"ƩQiirTs_]R] .D) =XER*D~gt$2)J]!{Vl   4k֞bA+|lq@Ϻ-oꌲڻ_{CMџ@if%9X5mrViq(go7V[*]w1I7>DO3Ӝg tf+e>1 ݭ7-@{rFS\ WDm   Cg'n_YŚ̂V3`FKX3s`y[ zkTSoo&f՟~}@؝ )@XX( 7Ugۺh<g*jy6k+ZIn. xmqbD "+Ѧ/܃%HIK(`[ojp]{ ^ i|wB"C? @@P NPO;GS\_9gs䨧Y'hCu?a$DO|H/"F u6:3Q;DdāΨ%Ousz}cOVFZ#e R i pwlB =@[) TPu!  @dp@Y@l9 ~ےRdfA&Гϣϫ/<}̶e /Mm Evчܢ,|v.:9KQY{z4c}Cb= eY@. ?9@ۣ1pJw @ uEi6$uU(F@@F 93^s&\UګBv@'l|J1~=ʁQN#҉ȟŃ*[bI)4 @ɺ <:o&t?2ftO~xc27tؗ~6D[ !2N(2 Nc%;[AoR@pxo'rpBԆ  U g.MW Z؎@ \`9-w{GppuV}['ػ~_^Y@ ai!$FNtDΖC .7 6 I<F|RmL 0m. lts<5@|&h .P   pɺ2~;&kOXLeZް[rM@o>lSEv+ ?=ۢ txGܣ?N |྾#:@k:Ow+yb\@o0#@h"ۭμNƂ@dpLwA @@:3Q7o#f~F 2;+Ȍځ93.n}Pv[w$?]<6'=<(%{]zDtqMN/,)5I˧*Ҩ|iFhWi ZpuLGgxIܑ""J\J4:!g.@JgA{4gJ zR  T/0t k%lqZF&`cł@-&`|*9:G/Z/V! @ L wo PbC#x990֕}/秿SD b! [3Ȗ4L ls=@o'=`q3dpu@Ԇ   #=9zcD L Krux:qI~RCf/jfnIDO{B t&Qdp p_,)5[dgO(Y,P\<  @{i*)2=m<mIc*#tFכ*_H޼=zԱsM%|~z+7Vyom;pX`^K!6v\ojvQ85?=w J[oubXo1 q(WNe܃WE ݉i/P$p-KiN $rR  T)0tv)b31]栅QY3yX]{t0&~{GGqJ}o\62ЙP=MX(D \s6b\CGŮg!|YN-Hc(#[E ;&6gћAS@DKcWU|7VpVr >1A QQ*ZT*a3jH^+uz2&Z+@@Bw2W&TygՒAҿkCJN_S?ko&ճԋ/Z+3ea+ JEmS:}UqX9i]^ݻ37s'~X@@%}i2Crd{p&Ý'*PI=H]<{lRn[+pX};N݁r{T3zxr!V^^*Ƨ2+wpxqGoR.iFiߨ\~ԏt]n_8xA̬ ont3۹mU*6@ tw>z۽ YTcsw\s|'LcogV=xwS4~”zb?;9 PKv2; T0zWGo5 v$˕5)UsY:T}l  $0TGv@{krfc(OD_*e5W\V69 ZM ~ӬwmH5V[Q߿~|׵`Y~ʷN/=s\8?ͭ+MLn2 QXdo߮sD<~UX>伍?q_x8/f\(v /, O2MC%@[AoP YPJ"`GbTȱh"RJk T  [`WT[{+b3B+ВJws:;3K]NsjxΎ25ko\ssڱSmv v}Nݑg99Ʀ:}qJ}'w_z ZpNp@ J h꽩HGbsiqb 6}̥7X_Сb)S J F3A i pwh2  ,A I5[ XoU@@@Vzk9&w+ #9[j?ާ3NB6l^OQ:j{e`gTZ߰21I]=IeY@ 2v|L¨'up\m, rV#byE b+`n8g R I i 8dՍ-)`RR #j(6[P N"  Stz.>I my"BB_@->%t \[bu1%@'kd  $F=53>-vj@Ės<F -a<9\ ݕ/@{yMS7 fLR  &pBsfk(<!ik6cL<=jxΏuuꮥ&xKgmpշ6E,,˃ؾwYy%ęը]RjlN>@x͛|UWΉ/Dq P@}jشjD{՛!DJSN@Mĵx] {+@z#ۅ   Œ2&?&\j В @ړf^P7M}uܓP)ue.Od1Y۷7 { K7cJ8S UZLkJ=]/=s\wFo NMH şFEigtf* C-$ϑ:%@mY53k7@@^i VڐjjkJXQȶʣ9{)&eJT@Ǩ] tIIvdl]K3 ^gS]ߑP&-  GN.䪍}*%AK{GOjorzsaSC 8] oa T ˁJc t>tvUQ| _N*M9&1;ΈF @@y|{=K%赞ݻogb=kk=VeT'`.t'3MFي_niV}EOjX1XX?w vH"\CepF/|Hl[ڑ+|ޡ_w .\nHl#۠npOw!pJkJ}Sm3 M dpד@@@.pw.Be)E &1k 9weFSOHnh: `;z,EtᅥA&@r] dLFgsi-:s r#tߊC83t9 TZvJEozԘ؃|govZ1'?ū(u{c H^ĥmY@Rw?,0 q٢$P,<<'Ϊɘ3,4A U>_Y},   @@ugpo 4M0}2ィeo]J4:o-tFz0&d^ϽwVt>"Lt^&x}nf77*,4tFZܼ?ڔ29ne&%f?rf\-cT< Ŗ6LS; > ]#F $S:Ɲ&@ef"R-wG|$%{dv52&T[|Ƃ  %pp[Z mG_ MVh̥{uM?/N/A:|(&߻͍ Z~Rg%=ly쒊XDt]abYctC#[VSjb|6#=ɠP T|4y\'zJCN* dnaI΄j[拪T  vz2we+Q@LKi9}HvHL#@Ye}tF?ވŜr\Ʈg0LIEFSgTlܷi>` p7Ob>3tc??6Ķ{ <ч@BUЯ}{)A$H+J#MXs{L> 'WCԆ  ̢j]^#n] F7Q_fVCLj x{  +OnJ|?kFvDTܼ W=98O'{hC7*eoXj%|PشC;4t*3iǜLaAdpW Om   "zᵵht,f~&mٴJ%j{&HV˞/_^r ^ɜ3Ǜ7:, ²a[o 0M;AaQ@&dE?.OU *L/T:~dn/#h%r!%M8tbT|"FnOE(r<$hno#FMQ YItT_Xζ0t>FT Aw_gl   n@i I 痼>tb4 .Ex o~džGWA뺫3U?y.eN78?^:tQg_zbFuLqEݳ)yFwdC" $)>0d܄r'lHηO|hZ9knѹ@$($>RU"!ݾʮ#@xps텺wfǚz;IT3;&@|o*ZJ !  ̥L5^ I}Sae:RɸCEZZnnW]2'||K1uu/Ig_{C}j:_VwoLBh²qЎM]M { \XPG< ݤ=2#67H{4s7|>Az,ivi55g@EU4m @sà;G SJxqAp#5'?Yh@_Ie   -`./8t6G y'>+VG &ȥSoU?]ܬZt PTQCjv^,&iI՛'0BEQDפZS"5QbFM?o 6''@/&gO7XߑyQ2iڤJ [Ee]@,<h\ks"%s¸Q9+L@@@ .L5Bs |r^'@#@Ɋ-f:3pc)dկg?zs>,&p?P@Hٛ6 W-#ol_~:kyR\ϏNR ۢ}@R BpDSov6o?2A-'F{9Qn6=Ј8ltm?*k{ `@IDATiVܑb.К{X8BpLDW}2!HTpp:x^4u@ w2@@@J2W f @ޒѷ9%[Eт1!,p\|}WVxa~?)؃~[fq' @ 2s5$~w!A'gXLwɽ"ݵ(f2WeF {Ҹ̱wrk>5^EGόyGTQ= k <9p@w&. D'{4)Jq Sʕ2)o`c@@Aek]Z(]#%>c r( q(@ T&~I]DLDg}UגCYR| lT    8}w27pVpt/AM'繷939OV"qKS~_W鏿'&wнqT4Aܑ(.˯-MΒ WӻtZ\Z쇹5=qKZl, j得ϩəEA܉x[ I(@{@4Kպ4pZ 6My ݓdp@@@dpAM@fζBT]Qܼy~6AZO#5\U[uࡴOܫ2)845?͏-bM&˖`qG pL`ђ}@ҝǯt*dUK?Yuu47,c!ѭ= R@]&Z+!@x,3Fnh75GzzC18&&@@0A,Y2>DJ tX^8p1Ru0m:%`Wm:u/])d՟'U|˙ѲZN,jep}\UC* Zv\Z,")@{$A!`-6"3(,@{ddpi(@@@.\Stn@r/ ^g{w,逸EKP\_ 8 U'I_-Lbt?āS<-k_ދF gcVq' F64ac'>&6C %\ @`FMC!uϙhpڋtZ͂@pw, @@n:& %% td*)%MՐ%((&v1 !;QYk;PkNg]n6<{Bw?tֹ7N7VtB*J)˱)%5|eFC! G6Hk| @8jl3V@2ݚ"XtؿEk  ?ԕIM   py[ 껙 nl"ܵ .FMs^Gġ LZ#[U޷e([숽NJ+Jզڳ|)iǮ a_1eC_z8{ b y(@&VPS=l\@qeHidT/07$z._R_)n͘ QR  x\:vU۰G+' =B!k81Y&A( ز#ۢ4Tϱ_zHeZ OQž7l7wIz-##]?MFD853`y};E! {R<;-"TܛO/Jp mdp2'cG@@XU \^ @gNK.RI %߿Q 4Y_M; D[d'25G~CTdu% 0 -/[pwm.3wd]mm[(OZ>N(_I&orA A#1 b0S-AhMqH&+N{4wԽbe/u#  *0| .ZՖ @^gl.Ɨ],GEЇg˶)DJYڳ8p'3Y\$K^tVql!SEugLb\3ێ'ڣ7$6ߗO(-P@Bj6[:!a< DHM&CAdB^ ۤӼIUu$>?+<HjA@@O#Aߴ2{MZ;'/%gookU&G{4@ZI I86cE@ $dp9_A-dpd /#mPű7TR_yL0 {pk؍ kcWB*LH'n#P靺 oU}Af ?ĕP  Q`trA-R (pb5Qeh|kl?mɞ9;/X=2z6)sdžmco~Gu2E 0/S髒A(ӓC G*'_\(3KgܒgSEQE (Zn^2NG:x̒=P-). =6Uho^+3ꅃEË T^ߵ I-C@ ]a=('pzj8*@"nn–O__/{"  t=K qhLSJzH3ugsO7ۻ-OvwV}U#7XIw7߃>J転C1|-i2oy!&ؾQR  $p~dߪ1#V_["@z`y|mTܹ^$D8-`{0E}`Pۼc't; . 2o'KsZwZK_lw'|螁ZuFpgY\>Z]>]uO6@ ֙+P&^z˶^+@w=͉"@@@o2-J} `lyz¹O)L@%# ଀-M]?b3:@dpl-{H1R'"P@*ij*v6L]D@@ dpeϫ#gzc;ŬŦq9Xi Ͳ+4AW6jڼ cSoG:](@ %KuNN(uʌYH$AۻrDK1-z`]MO U,(`y2j9:@oǞR@˄!t{ Edpw:_OjC@@p\H6 ԩ[囯"ު~G2:[QKb1 "|3}~q6,Mq' P`$}TX:xXf@ߏ|d)dq7`\ߡ(ĝ(@$k' kEdB&@{&"V*qVe8M$@WVHm   {sQz\C51n+}|EOޫ2)q:- ^`r(}SgǷ [:<1ga =9'H':pN!hnN(_~*Gx,F޼=cV!@i *$AXin.<  Wتr$~VR5 rR  h+u9 , j=99{GFjUuGW+j,c:XepH1z.9ߒ'VtvfUk|>kbxTɁ8QZSgT椽vu$ rgA X[kOT*w3ў_FJ8=>PKc()$S\!Ԇ  jxd.,9rc'&Ц/JIuϽ>yUZ8Yk–])+NDe#.`cw3\/ f.P\Œv@Vuφ/DXquFpOݩZj8𙋃Z씓Ά*Nc4_=@ X"];Pn<")̫٣  )0fۥ֟?=ךP!4@]*"$n( YeLXta,>!EnwVMf3@@@OW'u!=9 >>e{|[  \4Qn˦=kY^Qjɒ)s'V"5;@uMĥ%֗٦z Yi _0b;doi(@wTJ{cTEw^LM8E`8 ּ֛l`MIȂ  )Pw<9ԅ@:uXn|YmcWipJE _40 vsaL܀H꾝}be9S܉,`bW_u-ǒfOsjFlw/V̓8WwDK MKU7 pTwG'n!8%(AQӎEw NEdp]@@@*PwwNk["@<Lp{%3̿ծ-]XMWn Q+?6ߞ; o|E2J,/!]0ق4}{%%S> 3* lB1DLM(A`5dBjH>'H>AU&p5ˤp @@@`UᑙUuVi[Ul:EZiM=piN.TXRt:?;DEd -^܃X.՛–0uuFp7ۛ)E~6 27BUS?yE.((*cB&$wY3= zR  q0-nɤ@U~!@|-MɅXܷO|:KubĒں7 );=wyf@ ggyFزv30S9֠V~cbSF.M @뮔E 1o DU,B@r-4b}RqYyuŶ  %py|̅k@s20QȆZ9ުz;Rqחty\ldo8 @@E;/b&PQdpٔ3\#9ˠbZgCԅ  26_8[SuN snKFgbnoܣVl\*+!)i_M wL3`zy@p'{0s@+ 1gps/w4]Љb! p](OZ~SwJEȸ|@X A#$@n01QNoO G7"HMC%Cw= @@ Lm6infP=o~AJe#vז.IY厡ي52u RDl9IrY2<=9Ny, iݬXWXmf:fǔ˘<ݎ1&wH ̇-Fp#Ł2 dE۽,qW" @ 8t ]q# RkQ'ZBX+SӲ7#I]A liaU%MǧچISŸ"ͪ)ZW"z~W9BL5L^ f39fC5V>$@~q#\;>qZׯI(-tgrƉ @ gz y0[A 48㋳V7>imr_mVګPcѮSG 븓\9v, f͂cܣ1RWU8zy՛ "{z@簱am (PJU- 'Z KJ@ہ[@ s!pOwq'G! u6?Uۢ >ߥW#r˘DvOkL:"ȃ;* :=u zhI[o!vٜHtg,P]ApusoUv6!Gzx3QN !.v@ pmU#\qB-OJ @@ ^= @kE<{GF'qwE<#`'FKK"H r;Gx>6#~"NgdnreGT=#%zyHabzFCkQ$@@ol)$|:@d l2󡋱QឩGC @ Z'gdrFahnE/ hhMȱAO>%)ԮR7_N:׶˿pC 7Hfe")9U(+7SY[  nvw7A[w%$@ **}s>S;v7vF]kBz-r e%WmJ ,?=l|nu+ t`Ĩbz(L"a8%TE{P!Pͪ=79A GAL" w@ X_.!"X@}UBjq|qJ/k/9mqRVȟ}h~Y' ٵ,Zj'ṅ;N/-S՜WI:PCcJP]i^q+uni:Q4cP B =jSW 3^cu'No</ }!A *ǘB`ZԒ8|MsO#Kz Ԋ9AL.Ɔ͉o$\R1t Âw;aܲNM+0Vbl6%ذjwU  P0 xAu@`w5 S=A.D L4@ Ę@kw^bx@KYدg~**+}W7sw~wq1Q&UbyP EWZZYP"x-|?\>3 -iEJN>1}bZ|@J aUk! o#p/a~nUlhMHfv}=ؤ& pP= P?,&\`-wfs {lHl@ 8" `OYp34Զj.qωտ:b$PVbMyirZ͢1!e ?02-}CrzH}̴n=nuZw;`ȅ @P,̤5@ *$O! $0;! %[B @\;kT@dVzG20n^ǿ3nCnS J{]c.oiJ %YwYW)ꊓnZ_gDxo!'qp_espZ`pZH.bt٢W 3^cM5 乀 @Vǒ ܤBsaG@h+=8(cCfyVl$dS/ @  <?E@ h_ %jW!ڞkRnR#wrܟ^`Ȝ drwX)o)%~OMeet(L ߛTO 5ۯwݗ8>z3Nҳ n! n*Zk}BspWQz笺1q))x=Qnju½ȆN`,oj@ p J@;G2P$~Vst))wmFA @u[JlH `"0:)j*nVXsϫ7oDc9ӛp/Kʞ#J~hE=2$I6Hܟnv bS"2P4C0J3]&Lظ:eN| @<{C#z3N[QW}1Bpk.Z#7ӃHx'HZ=.NO(N)ģ0@ l@dK30(ቴM< @ Xp^=@JsB {t~{k+F^۔k3>GMrՖsgO'X_gU\q5N׿QWgOڄi:)xNʅe;F8I@Mw&5_Ϭn 97 [e^ɏRZnq% 2Bxxʕ կ @u̩oz oCpkj2`%37)`# @ ؉m׊2*1{Iwm-r'y+7JkcU>E7Y^{K/)I;Q NJw%z#oTG3bPi*-B 'q68n-Cdrf_0̙Oaspoo i{{{e>W=kmpP{Lho @ $ [Ġtq%=#O!@pif*Yc29 @ `IWP#LZOgfd*_j מ)oS6  "nzGƓq|Tu ߻N G'aVRe_d^m)rVy'`7b|ΪAQdYn& ^4qZ Xw]HN$2RO &rk^&@ @ _<=!Pӈaݣ-;m^9'jU$mMR.ڸbozsyעw-] '͎Y$jgԾlZU"5X{{KT`єg$2jo6WkT-"Hν}UkM-*+;lPqu{{S}ZlCςSacepH@H7[m&`C%^G6*D&1#6|ɔzF@ppvpp'A @ :`' '9UNJn'rBJv6ʭ}&'Yئh3R?=a~Т)%~}As=z29![KX`>p>By|FKc"^ &(7cYRȬ|IMiMHk]BXQ<5Yխ`M&XiչUos2\ !6\cъ5q%EO{C SB'Yݧ!DB-jq!_VL[@ =#O!Awekm7o\/ڊE}Esz)uV)=Q"''wvɯ%teed*#(7* HԜaT9NВ ?N铫ryYY&k)Ur\*f8t1U|?>1T}y7l7!)B`8ƚ ܣ2H$23,Xᖩ$XS~I@I$@ Ę@  P&'v'[{%[^?'jUdMNKሀ{]~n}w_"ӳ=دɃ11%$y,-- GUr&`'>1syŚ!93+iض؃wg&F93=!1Ty s&OeߵX@}{|ا;tugʖc߂H{dH٧fªF74DC TYr@ p/1X@ g?ZgLwo IS @ P4z\' Q%'Ɣ[܌+{>T^sy-Vβ<·'y*Ԋu|?/ /xQ~TxZ.>T_}$PUy#>\XEOL]ڏf^ge%@rkZV;.PWeJ ?^_o۽H;vX͵}D d 35o-|"R,J (I% Bnt {!_#=<)  @q%78evL)֮DYyg̉tT_Ҝb%PV|ל+?-ʏ]J:%vWu!p?fB[BYZk1{::bl}b6~sp_{x>{]UtU̓&^qjrVk^\ͬMV1Ll@8v D|@/X@)NI2A`N%GLp2q8rA @  sp/|@!bQ>iQjE Vn[jac#}ȶ ȷ'ɴ2Qj>"80Qd+ZPUL؜J:SOۓ{FV6kˡr4/4kÆMg乃f{}8mKr+mx2+)o ߽᧎Xر dkl0 |#R0"J{DfA/Lf&.)7&DOwo-OJ @@\ _^hq;uLt@t MgSFj|U[VϹvO<}~Mr9+僟T2Š ե.lDD zVb)帩%mڥdd^%Ea?5X#gmƺ Ifd!*kooFJȽ(]fLqp~@^cU*1XE1عQnzz;6 LF<$@ tzw3_$ j$K PbF@99O;=Tܗ#[zIck@ @ecD~:]YʖyM.־auCTL 0G`&o{>#B%e+3r"-A.IcnQ9wCK͉d]6~%6\fxϔW]^j^P'}}r>_Qn+rf$8 C=V\!{ZaH9n?_M wbJNSv# 3L6aJ TL廳+:@ DbFg?*0Td$@@ @ c)˻K}k{q$*aPFf(U67]nέJY)B`44*ewQnwedzŒv%uֻAஆ|>)ׇCJԾOM{>׾}w_*UmtlpO<5 ֚[VB_|Y{Ayf cuUѼW6w7U7m2)o?x\)W[Y""0cꈫdK@]em0V8 7_^6:ʢ]?ˤ]/̘gDr\cu6#eR6Bխ&Oٜez)5`xj3VR0! ra`h#a{͆[ݒ# 7@;r#[E k< >F @!04]vo#@"]ڻ[{ϸ_]Y&9aV!bӚ<rze)';wK4D$P][?==Vn3c}kr'ax\}j'/F;)ՒSi-*!wIs@#ƚ+.GM>5]Ց}y]Xv%p'@Q&t5H{QPm+A^PEc!@l"1@营M@ʜ}!|/AC @n͆@&06Ρ }rם%u5B [n<(pTP:VRM9}n+l~pf@: O7t|KE>|rĄw*~まl9`~I [kf^P)5"b3w"QZޭVV2_{?DJHMw#  %hUxs xH0) @$I){!4hژΘot$Jx p'A @ Fݽ)+N,w:Z>]J>nBMUU,Eb'puR_[!c%]ɬq~[WmG\YljՄr%rѳK%,gBE fE˶̏vYf{'#ǭW6hFKkuɾt so{>FL_`J ,~D?0և{  RE S!&=~L 3E[@ 8v<&]I&@@s}Z؞wDV4W{n"PJw HT^{y}\{܎H8!Av[FE } Vў%HXgk71l植oLWOX\+wq?#͵il| j%C`Ac5I ஓ_|m;/\`J ͮPK%Penn}M[Ҕ{1S@ |3q{mU|Q'_۹-]?{zЉN $HIƔDvw6]eY^Aa"i7ޮnӴ5FA@ $a>>lݲ !DxW]@`>L:fGt|{TE*ow~4@ đw18qoa.ʭWg\>{7|xZ8W0Ƒ:Czcc1!rq\#r .\kV] tVd15ϟY6kpVyt1]ꈯ+d*+5& &Yn5yTIV #n޾nUuz&g+ioD"tP5R#  LBfh33Kj1gܾίZrC @ED;uh/+Q$sC91g0 _M@QXd0 . T g'qYjqe;m}2vT dzT.baOߴS;?5tkU%SU(++*$|&0fġqVGgRǰ }oQGZo$@/HDxRcb!AxP nfԌ6Bz BB$P3D=Ia @#120Kyɽ "!3tPj٨ 5Uer-}/kmݔM5RYQ*I բv~<,F)ov5)345:υѸLþތ O:;?]~Yk.Z#ܺY{,ee]H% K>#i froi>>;2dLNŊė޼ؗп䠱f5Sؠ>hDQQ dfөDi5Pb#{Qzpw?R& @E`hݻ2kG8gG#2tV4W{n"xfitU :ewNlHw?|w6e^@ODfr`f_mg̏GD>]*Ղ힑o~d@R6znqR@`wǠԀK0??AO5OsDv;ddʮl@0Q?@ᔔw۩P猪$@ bGl@h&P< *?g7-OJ @@ uXqo~NYO{nwM7_(o,(gcDG`UKQ~ybpzT[PvQBM6X3Ok*&՞M9CM>+8 z?fټm|GYѯVY\ S94cB85!+Hhvkyʽ [e <!͵ Q !=dpp:5B \O'= mZabڂK6z;T8{˓ @ GCc a]>{L@ ڵ] ݄W\Z> ֪ 'ܴ<jl 35dvo}΋|5"0K CDg=#Iy{mM{ة0?M YTER9XʯF'W-$Xގ+ioyR @F`h{2)ۮB= hqN.FM}fy\- `A@LFhB<l 4VzG1I9xZW~i@O ӫ4 QOdoىzݪzVHn٩"_}ٹemY9%#k첒 ӻO-ZZ{7[Z/jJ,^_7$ $p^z`2)wVn(Ag} @dMm[\-m՛# l,76w#\L((!Ƨf乃rKci]\rJyzoeyOyqU1u'F1OWl}eɓZ♋LYfOmS,wwGۻ{%A"3?aLoo+# # #@{F>B`6MY ${J7n0@ č[wqSڥW9Ȉ@ʪV}w_"olg8E e`EZ24V'd`ϟ={vϭCm_k@G{ԹZ5 ޢ2^-wܴ)gm[wlQ/dn5TxtIE.nCWѢuξ8uanz\pv!~9˜L Y%n: 8` `FB@xϨ A=HeE@VȊ8q# @ pШ;R^A18}ۛ+w<wn9qd  ^o+ KV-p7-p'3~swx#i9*?7ɝh9qɿ%QD#`_ےAupWeY#qoKʏ~a>KcHӲYHȏWR#7 rh4">6ʫ2H`VO'@ de%^@ ēK{9uaFXFvȔY?2w߸E*+JlC 1{)8@SK_Slw7Zܮ'w?ו];Ih{32 {n;_/n^H\[;R8XM?˖*)g6 W q|9/]+W>N ˌA3^-iȏ>w%=PTh(F3h 0@ГP&^JK2Wm;UR. &?/?#EG?:&-v?*LĄwtŤuwTWHE; eho`#OLe?+Q.Qu&{.UELA Z&3P8Ek5YX['r]=ȿ=qP,OfY%rԞi:N}ʄ*#ЬVWˏ^/7_.l fP@ {d3c%ű->9bzMc<gŠ+/ɣt,h a 4&JC@He{uk(@B=I$ Sv@^ʞ=H!bG`d<%/]0!K0R.|L)i)Ef&#pwU YU Q-ʨkj7k/>Mdu[NB;~_<_\Q}N6VU_ 3:] BeRא!lh\~n!5g˞~{âjͣ6@ nu򨎬" Cc8tJnJ[7B&K" w0) @ p5F&8!q xq?08] KǃxVNޚkq˧}l=/"xLK% }TJK6DO`9raA Lyدg$+O; Wp*ime>mw^.]vx>gRj+ZQqppހۉsYWǢBpo}F5p'  $볉etZ5=TNh  \@# Jaŀr{@Ǝw;:/ J[/*b#Đ%wě1v7j*tkqŪg+OݻU^uY8 rxHȓ@ZO=3L\1>z{?ؼ\݋pfre+)̢3Q%I҂)]mqآE/~UrՖվI_?`ܴs (փ$uɊ=XH@aUJ͈G8cKލB;XR 8)h{!$uW3 P8.c/:ݠ` h\hVd$W;.;n( vEDԚu<$@)=Bc=1=#z߼䜕N+Z[:y][^/ coLpݙW:YXP&:V)BQ:Jv}w_VӖə%:BO.ZGF D{N8=#ƌC  ī1+@ ^emP煐4R F,ߩrh*?qvۍlH@`Z#l wR$N@M&7/kņ@OM=kv @"pg坰{EWͻ x}Ƣ*RdH )I&FSn: p4E_d)Q&P"㒛 R{M:򝢁GlڼgP  BRw5 OU܍q֍g^UZbrsْ$DA%l南.pV9wCKѠ+(.#?ox]HHڢ7.ų=r']XAS_wO'wvRm~aM}zYd\~^:vjg{q‹Ӳ7܇?)5FJX~-v÷iK, (83=(FYQ  p2u@ j;!0#/=X(6ٯ \ p.ΕqpwǍ\pB7N(-&߉7nu+pp_:>=_{8+G2˭vfԻ/\]__؄0_:ݳ$Ф\ܵ0*踷k:_.#FlH@H"pv2lD(m@ԂW"_ܼnBF w,) @nB{wXxyR\#VfGc{rnwjL Ca̿ݨpsX>ִ ȉ@ *PgX׈}j|G 1ՏFˉc67-WjWenGk,?cCH&0i7&)`1DVF(~< .1 5e5QIW!=bܽcIID!WHVrA 8.giiS).8R'H:'qM[OGt n/q#JऱuʍW R*>ӧqqj,PO kJɽ]o̊|<3&ӳuId[޽=J ٽ]kSA@8L%ёEVcT xH[(aR vppMLψvLu w#p%%AF]NSm*xEnn'*RJsDFIaEso^-vz4Aa~aav}տ3 {XSu)wݼ:XH +3iubkcU|$`w{uMm|[{ #O5fZTV$0&ZD"=0T@%Lb(0ܒQ״ji*&,"n9,5/M$9ΰi'pokCl+;;c~ikD%w˿=~ȘkUcBX͈҂ks78!% }i?\4@ .F3!()QO H#@F;n䂀PZM^qp_ʔ"=5s(+N<(/v|-J9!MQQK3|_/~cNoZ3eAoJ˱ MXOzwǧ`}sذÕe`lAcA.?)K߾HO{VffQ)d&; (@/L"=x p}h'Ȋ]mK0 O+h@G!!8Uƹ+\5(-]HqibfmumT}a7_#gizh "N͇opr֔(b# W[W~yg[' e`drk}M_:ÒLz޸wKh>EN%geMcuEp׭7^e;Èo^c+tH@a()=SAI}@  h'xB oP *L1%]2Duލ݃Vj$@(6IݍhF _pkǥq;9&!pCR:?o7+* C l '剧ۏtuʹ%k @ tj1N@v7QW0؈&-{tRV7#cI#p36@J-Kj&PV2݌-E;t fixN 2/Iye 8[@#jε<@DKA+ 0 |Kn1rA@<e(|˅;oX*x%E@Z]7فP %*WڠddZ&LiTZ{kTv $jw{^cZ +Jn+ n~ M׬O;mܹ7 L|D͟=GAzy6y g9R_o0 Go{ԩirc@ 85'8WT/o@pLscT 6)yAa|ิ/ qpw==QЎB]M|+,o!c#h7:R=}DN2i>ڙtUcBxzP #V9aA`>pYO4-X ([\[~z2U>nӅ םiռP/?X?# PfM'BXd۟DdM | | oH`\GF>C PZ b/s%Mʂ %%(݌tBR7uq!$Xw#K;tƋ\ʑck|ǿ ioEձT;^,hw7(a{CTW_5@Kvznwv7f2&>ޮ6.D_[Bu;9WFjۏɾC&dfI|'N^, B}@ >g)P&-Պ|^*0 -, woR @neE|!t:?Tޞ322xD ^SÓ5m&7_ΣP D~%տ:ArړNMʍW! UzBx+=xA .ɕBkZ+jk«-8n#pW~1bTCc\eMȹ/?ӘG_7!p7"E@`U݋\{FA 64Z p)N$HSw6\ PlnppwǍ\pBJK^`…{),b|'0DZn9$@M*Eꐊ=jB{ D0¦*KvO-siG @ͬ,t%qs/iSWxvXJ@F%4T>zܴ|#ڝ}rgBvоcOq!P^*RkznvoSJ4 뫴ła3υ9nQn>*cm% +4sqvF܍|H}@9N?xz=v_e̬ ۍH@HΘ[Ehu#=ZBk ncMMLUh2V;Zk%A@!)he,J" D}^`b`YM/o,/mxFƓ^^s +[jmqu,ɎNy#c2ۣʔV a݊]PV)-ܟu~P6趫u\/ G&}N&g% []۲܊-;~qWO52{.3%Ȑ=j{MS tH@HΘ]$!TIS  p)%BPHLXUA`)DuKl> @ |j$C{wcϽݴ fOr*Rz_2;ःnbN譅Z>=wƯ]%uϧDYa5Sb9T}"vڻtT@aғ]N4&̤ՉZ )DAݟu22oHV4Gil>n򇕱R PL#p{BcVHe@d pPL   .*DuVT!sώJ #$uU! ##|cN. `_F¢-8^7ʥWΏ3H+3N#<"+wKrTТꊄhQc\>Gɭ\`t3b \!ܧ87k#pvHɭ6[Z))6l;51^{6zx^WWy}41ZלƆM`w@6@ !c2tppWT׉I\8"x&!s !@E ZE!; L(`0<@ȴ]Β}hV*! #=kN2󊖵/X]>Կډ@`9v^^-W'K`d<);.SCnB3?c+pGKCo.oLj%i=V~^MNkR- @,L%gct2##34`}6ĚV4X[fN#"|&pґJbT|>1DW!@``sn9`M^<CY9ڌVWɟ~jM³hbE?O=#ǟO9^ԚR,"|)Id~*WYkG9z&;1[LڝA $A&χPWJ%&6[x|GumbńQl#BΏk1=`>+Ԅh~dZ+ am34DC(l.(wH=6/5~h #>¥hD@dP m/MLCy;Zm A@\n8F.8!Nmx3m\۴Rv-nsɜ߇ݾEޭRQ|.q<3G:*+QO~it͕>5$Q;Ξ+>\HΘߏ]YTőy\MYLצee%„{v^vtuUɬ􎚏.Z#7^v˻Y7ZjGb#PbVd"@Foh)'ov&]:Jj+V|D-޶ @PJ?Gp5m.\w4#KrC9$v E$hQ_rhJ淑wthN>8:ITrMJDOR v 6 j˥w$L85,0Yj5?'s[5) yb~C{dx,i䰶ߺ (Bee-ք p9A *8Fe$h&Q &0rG<|M@ f8pι N0 35eRR-fA 8#8j+D'AV8ٜm|{}'%)'Ʋr/#ix;.Tn Xh>YC ?<%_?Y>4*wVެ*yˆR% `E 9k~V ȅ;7 @f*̤zF`rzV'-ΊRˤ#{{Ey=I{O4Ԡ^o!xD@£cz H_( J hB9 \c%8Px[%A@@Hn\:RΞЦ9hmdI@ Ggs,zO(n^9?1"7.]͇J2ec -joKHkR>LF2  iW5kV peP R}7Ka@IDATjkݞwz͚r'F2a{o;_6HwaתUbCw"iJe擕5Q 5ܣ6"HfF5!@-|D-޶ @Pp.v7RiCDr'Ͻ@rDYs8n{{228zug>Mk+-}tXg{?93VA R q@(l+өY^(#jSW=k|sZqpaj*ERȀR>֋+d!ck"&zxgӉѠ>@ <Fgh 'G^?Zp[^[緬o^9ppvЎ @@.Bm\ +L|\>);{B!iНuE厛6 '/tTv%lΕ@B i5Bav>u ayvHM8/OЛ-'Ux(VKSݗHC'~&K+ Izk3#AI}@4xt9 @ QSGO7n%ۺ'02==rK{ϘM&n7@F.8!=JKحq f)윸N$t*זZ>J9km(=Wg]]EH`,i$YQb˨@ < 2u@ ۼjtquRW>OBa P0lnqJvW# 8|36om\8$с\_R%n\j1"k|~'{\Z6'+|d"H`x|μh `iR b>^ynue{ #O5^TjFF:$@ L% d *bBs (lvu#7h^-VPS\ ^vNl񼭒 @ 0#$uW# ܍u>99 }=7EN\Z+_[dY L+yzV- S= b"`'p5U3wtkh1&E|74i,;_{ljL2r/gRW7qlDb@`b {Ü@,pj@ ppL Qm(nNW=`~8P}51GmK(  @T\  $wiF([Ygd|zm?E[PZ]H&gK]2T;JAVk8rl ;uVH]!avpoV+jD4î(u?Nde@«_AnJ-o\W5&L @| )HXUA#=bBs (rp7Sڔ8T}b"ݤ%<) @ $u7ʜsq# 3խK!1ɬΈ9C륺žcӿ|EW^zB dptڲv}^mv/,#MɊR./Gk]nr>?C(S6pVCjgdOkBQ;&&¯?{"=k./G]GF@ B XdZb2ߵ܂k25+f>o[Bi P(u>#mL/r+!+dEe|K}+;:JޞdVw%{.G#_Ȯi5M Y:JCF PrU8!{{rog?'EfW[*a'S2VKpl~R_Y<Ѣpohz#^X(W׌q3BdBpp;UB 2Gf(hB 04AS)E0ގDoyR (So8SNiq⠟s@b$=;p:`n,WPV:$y_:YLXWLϥ^׮MW{u4!p7Y06&[ )cEsNc9ѝ_{Js!LNWֺTWIbBU S!C{tƂ@ pY஖r 8W8AwFo#j%@D{97wf䀀S#8%p|&vpA7c32j#; -9mEݩ(-B;~qt8ޭlHȆ@q$ϥs7 }vAd6m=Rgk:uM }6.4jbU[V[nu7<)1+fn=M!(p%,f?J gFf(hB P 7bA :K“d=H!Fk\<!iu=FE%܍x>oR3BZX-G8'SYy;#%|:O(u ^ o/+\rf[Bꪸe7M5?{oWUu}^$!"":XPA6v:8:@D2(M3@ RH y-^nB}9|wm>Zj3xy!η,$*{EI~d~Żlw9ެ0}熳ȍEHݿE\*qMA[ik" X!{FSpXVg,dJY1xH௑b4n#H:k-Ak  ^"g \PQ3X.2Rp(ƄRiV`vLpIݫ^CΘW_^>}w]q _Ua*04-{lߎh6lgye,Cu{ɈٳMҘYg!n*"wY:̞f Fq2013][@ Dg,eܭ<@KWj3JM7 XR 8++tFs{cwvw'j!loI}Y^K+gPFmjGmQaʎ   )* q{8"%*h1®>1ctD-Z@@D 0+e1TB "oƦlX%pKAMqLt2/3K'0&B 잗W~p-BY @@`2)ܹt[pYD&m<|%ˌpK\ޏ.=k!FGo&[,O z|:LKx虜 &{'݃@ $DMDKC=L l)j@w 6( @ē4NeI`#XF%(cq^ 5KERس`GR[t*ͨto_)gGy= HHNp3 >oN`8MXH i>vTO>ٗeC9  ϭ+m!·0̇0pN9a`@(,nؑ% ܝ%@u pwݔ p@B޺'R[@`D\ IMw PY8.yݕ%o5=Gnԓ}W$t˗B9xY9Lv7oRq"Q3ˆA@@EE{2O}ۙ TՑ<\ͤTb}"3K4iPEp(p'**pj:m U w!糶#@~q1=(~&|6.K A~rl=|'ًPS"K^/+NI"%hL| 0&0YTS !Ht%ϜKYYGb_lkYe!ZX&(0 @@%ß{~’CƱڻh-n>|} >$'0xSUɩٳU},A28ѽyL9#-lGwQw~N9W,č@wTZK#Kf@@1A FC&!x;j)r]QF1/<ͷ qX" hGpOmJ@n@@g]:f5^OϾҘ,biRBHA@FyKyc L!06?BR=7mMoYl'iΐ=_Nqv @ )\:qql 0#NvxCeDYQTQea d|Uk,#ᛗPIavNU|}>v7k2@ (N``d N[F !g MQܑ"PPO( +>AnE #9Sͷ  tN;qKɖ^c\52sq.C}cRz^ rK>Z:!|k_}rw\e8p d(#hE$pً?%$EnOq-šY$Vd(+ qQp@gۦ.1 @  p pq3c¾ƾu,un\8-jz  )l<ι晡!ZĻthKОs熳VHv S}Kg t0e Mv@VM98yVѸk lZOcTv8Bt&/*ï1w}/={!d [\ye/ўn9Mp p@w?G`8!:z#vLLG 14u P4Gf"3ZKܭ@@ (p>6?Ӫ[C c x4x,T#U6!nݜyo<>1(Xi3/|eu +HAaF  `|*VQ/^fZɵ a*Wac( SQA6P: w-*Ȧ~":} 댰%p @TB"bA"ّjGh@[[kA An Ń֞ 6@wkʥ @@@,DJ6KHWHj'`qMF4Ui]߽lz[yhd0GQV/U.3F#\Q@fLm?v8B}aK=r9%ڂ\y7w5zVʎ//p@4/|#䞣OUE!:nD3 lK4)/SJgmI?h$yjV ;Rz1~yI.NiggG(R2䏶AOX e]݋#|wn.+j   57cx\T=5|F? a߾ޘZ(Z?Oiz})5KeU&%(4D*\++pt=g_m$.Hܗ* pOԶ1MDZO3KC  pu ZL bhHk^5 ǣ7R="d[L 53-%Dc  p8ӛj#Cõ;ݘH|!CDۅ=K^C׽{UDv߱nDܾ* q6A@=meUoYzo(HXHyAH0f#  B k+N2:&b1Dpw97@w7 l 䏅#wZNwu'Zh @ABRyNR5Z Kw) .J }yn1*ľv0FQ>Sv`&_߭lALG eHB@*6+ RQAvLobWы[ʹy{3uHˊsѰW,OOK!}y '{ [w ժX%&ZBQ `sw?- sb @`*V4 ù&CT?%fc4 }}sT\$>`$5"n 7Z">X TFKEKz\ VcZ2(>"DLUE1R?x~J壗g7%(dEK[ԓ{޵4$5j`x促mG޸ʹpV)t%|aő||8J`+YCyc}S*NH `ma̤وΐf@ L`{ZqGZ-2/`_L#i[A 6Cn ZB K0i$ |0L T0.!B+=artցLVo%OE;}`D}No|ݣ ɟmlSZ%޴8v%$d٫gR/I?_ǿ8+nO5V)WOO_Y{H۴_ʅ[ʊH?SH .y̼a9(D9 A#7!gz2z;i?c1N !n/mH]/8G ,YJh @CBRuZ 8pћxcJ˹!,kb5n4{r~Iِ\<{QB&XHGgW_2˻?@5M".SXi_ i\s, )E@4ƷS^g" ]j 9=Db oZDpwxht?t Rcu,p@Xz!9DopF ݀p_ ^nO!u0z 2=nFDxkӱeto4;|w8u<~lNv]H+fCܮLAEEt6|ypY&n8/Q;qM,ϛ^> bmSd Dp#ZIN@NZ} DEy=' yȵF p7 e@  p$c `@dw$' ;ʂxŲB;P!; @ Hp\қ턆+ez3]#N=jqU~鉲ee0C L9?v ʒHO*/Vn}t]Utcʅ;>31[Q9g|z5*lPRe#m xރa28@ᡡ3Z R:+\7#p,Z `F(M-pLmĕHR^שJwx+g;kzO/r:9 *A!@l p;ζ*x6+;wh\)Ί.~}c|jW|"KǔQ7 (7D2;&ָ2 *L$H D A& >$($v:{,hMqg$v!@@ IQIMGHU"aľ⼦J9q!mUOtuy>.|&gg4g]̨ 0/vp V 3ʹ6) %#S;JZLU@ I 4f :Z격+WF.@P @7 @PZF&Az ߮-==.=k]& Uۥ}; H%$dv¢V̠UKlw);X$YEDFR~*hTq{j NIu$zM'=1,̮bVBL ;86>:@`w8ܒy``"8A`dx\ 8>P8stYjc:!xzSz "j&a&4L 9QZI7AjrV(A|wiuJUh  9] m0yq{_M4*sѬ48aqnN 5sT@yvxU8p43P6&TT#ytRkAC_NpH86R]1S@ @@ *fWscgozc#&+pLOt&  #ŨΤ 5c p7R:ܳ5C?H6JV>"+gҷ;p*y2n/[0)w^%^8<5@8_-UJ>oJ/lV(M+{.b4?8Otaq;Ff4Kr5ZDhh飇bY,\γ| @8~e&P<P@p)Ƀ `9DwˡT@.XHw`BnK A"!l'z͡pGǛ,7Ä(+Lg_^@ٚsvؼMJBt:^(!!@ 00nK׾k%yo`nR=k* E}z9~Fز⽊XK-1z֘K ?(I@yrQs)}w>剂I! f J3(H p$Q'aBa@K'id"aex>I/uhm|PPgE4 G"lG?/5tYө2!(,̅M  ƞ8qCҁ#X%u`}햿+\P_KҮj!f[2A@A n.},n=k|@qb%+S]o9Oƒ2;=rū۶Z'<tj d@4%a[@ X{4䌪C6&'zMW lDI6fA@q*Jo"u)GBT.[z5gXݝ?_Y-HPPQgOhwoࣷHFpG+aq`E'hXcO؍ \w1rOuخM .t jGE0C 3Te]Ey  [mg@IDATI92խ"@@zAB3zVG\C=˃[$A(?p*2x"/7 =_ZFh].VG;W@&ŋ_PA4n,mךϏf۔V+_+ 1$Œ*˦x+ ~?=!Q Q p71%tǁNh A /7G>_j4*) $E@O @ 8S;|J QӁvX=C "[7QŃXzAK  ~#!ތFJ @ Xx7ߺǥ ;Z1k'sNM7wjJ?VHNS  =C SDVXx3iT(̿g.&~mAjQ*޸5Bۥ=쭼$מNѪ1 * ,d'-,H%uv@\OwO g G]Xp9ut#+M}T.  & \l!Ej8.4qIG@eO ['&Hx|bEdHi鎐" K;Bc  !g3Z ,8.ͷNɄ&%ߦ^}| jZ>>v('-I"UlNȐe滕W_ճ4PFͯ SN۹'3* i*#̺M(x$#H\PgIhĿz M~;@)n= Fϕ4E5k < c "CcG^m3Mʎfp @bJZh @@bI9Wj8.4q)+K//j" nAճ; |F@F-*G@ r~ʆ   1~KSqaNZ}8YyݦFˢ,|ԡ`3 1}}`H(X>V:35Ƣq—. @K`~͉._6#1]."H"u B@i37lgdr^ ֣vCQT l6  MW,(X ش A sZgFE6 #7^C :hVsD{ CkKMx ݨ{@IZt4\HJǥLluv\/}\/3$=v9_8 ?lm{Ycq3l QdT8%~$zy aw>_v0 =xvrmhI5;N -꽳Dv;Wvn%Ң\z^Ai6`۪vyLD@Tw{{Gm0f۸)2@|O@w pw~½ALV7lL_btާLt>Gb;DA> Dgv{C`m7h @@Gp.֛L]!^o"| d ks2uh$JXQU u; U\ {BS~#pCB%  M ?/Wc hGE-M5O5U@|KwN-iHPX43Yh(o|dD%Z@d9Wj8.4P7;|mAؕ~r)˩\6{}H":+xJ1DⳌnWb)=,=H r н D>e _7ߎ%snv7 ~LLZPiDx̿7l'y,IrqlC@T0%Dp`˻h-,Yp H$iԪѪJ &=P6!H"\)"ba$m|hh @@M)Yo6]j`n4PnL.!cThHj;5ϞKy[qRLn' "r1X,#ďF=v\vQ:T"j*"ghISwb)jg;Y{ei6G aŏSf5cݦq X<:ؕn'SYG hZEUTQOzzD(GD\wt,.gOT'ѬRd[zZDQ݋Dtw$8LO NK8<)G~Δ͖oPDo?sL*)tGߠ>b=@U1I,3즢-{rc%@@Nv6%bI6c@ @zœYhtӘxhbF|хdJAa]7h @@GaxљNu#Uq:q)+K<`ءx{Koz)TϏJ?O9!*o*$l(Cv˦;XTI3 ERy] kzioC7{}ߥHT7X2B.UBGDSAts=1!ﷰKyzׁlLJ~ə ZzKh$I"=I4lTl6$H@:&n\vq? XL@b dCn@@xAƭ 9Smh(bhHOe^ S. S>V\0{LC0iތbTF_eMNW3ŪN$J̴.ۗ8ťbg?hۣ1~,=hC ]Fn\J( &Hi"4]=ӕP@ pD4c E@ ܱ:Jz-L&2œ[7Dp>  `f*s7#k2722NBԺԦq͙^LkM%-?%ocR-jeA5tƩEDmOX-żq"ZQ sg-'շ_{Ev(]1%Q|Xf;h\r/5d¦,ND kw 8-u4<*IܽpT`%6F`Wm׸#g2o8@!gx;~յ Vd.%JjE9sYL:kgWWbh @@K $՛X:j!.Q C $ 'xS Cxj$݈]yJLDmת.FϢ<<Ą0R]n.2,# /OvKU##KaUs'w=|-vzfʔR{Pl0M/ P`U|zyb2zBΥU97KN@,X_np` k l\'l͢\x¥jIgEܸeÑeT;(=!Ylp`j g q0Ȩ͵ZI8Mk0#}l2x@(K$I|KܴG)'f'/꬝m]QV5/TodV${Aq@7;Zo%{[._運P,GŽG]L؅H2bn6-[FDbD6;TeT߳^~-7x~SOzlF r UA!B/]NŅޘF*{9 !RFÿ.37F+ccFT,"CMkCo79< `)5ugۀ7j` tcPbxaOsf9)z Wn]QV5/XoT @84q0_Qll9cKSw߼r&a,ujhgۜS ]R,z+N bL:ę+Wo#-e 22;YD?q[SN-\E";ViƣoKRWrٮ.4|h;kۥmASQ~6k3-ЪHy\HLJcJ{k,q͏ Nϡ] Fsk'|G8B#(@`"捿hߍ:">EJ)@@|CUuZ H0^Lnw$#(v p{7MБoO;00 `!C@06Fu ~.w/ ̅= Ң\Ƶo֞:'I8lS?L-"0*- !.ZUX~@]cGcD]8a/Ȩ$^OI3qvKrq ȽbO6yo%!*ow|@V7VA&nkyGKe< 䁁D 'b{ſi hnQnA!QFssb  Do9 ֤. A%V%0n=b;8-;;p]ATgZxzzP JS4CFpW Z-R9G&]2jQ[(\Q!v`9} %"7gZNI&|˛9'ͦVROW$[? (=.Ӌ]<Ҁ@1Ҫ6w*7AJak=;j!pGrXO+J pw~F壷s 8E`h?o9eC 1d p7 @ ĭ\9;SGq"'Y;q.4ݠ5!Ll©L *!!=6BǥB?cєUe-Rt:_.9,{@MS/鑝R Ʃ֠]C7g59-&#x?_mlUkbƠ?18#a_x<:~AEFl3i]}VU!Y1=_~ݦ*Eh`ؚ:N7w??=sy-҉mz `}ZGk-'% 5I3@Dpn >9vCo^.v zYA %M#!F #=.xMA :|GNӐ1 ~-a'Z{O&3規N\\5i;XK KXVϢijRv7WcbvD`}$w Q\ Ed;Z.a`/?o1͙hRQ#eJ^QwfvA ai[ -Ix$QG@ 5V%H(R0g}h=ж{Q @´&'mZ G8m{nVȠd!qq^E7]f*)15w( JZ"BdxQΦ8n1&n/Ǚ~-w@lwzW9'N٫дF'Hg.]f.oDm4 }`<G8 c7?B2Jj;_28Qn9w:3۬2ș2?KE ??W8AׯhÉPs'f jEt`Jh@$%VXi¸4.;gig]'+Z %T~c3NIy%]vd$ W\B_zު~9"8u۔%j;E_KC۷d"z{"zt‚ ^f;U 83~f0qztkCXx `HBلVlb$OY &'~ RO=%&k 4Cn-Qv3&aXU&֠C+  hYp83=BR&=Ǜ|/b\@/ "%6t6|5szi僌"|5q&#e&n~ofWMg kZM2YU]/7 ;0(CfzCI>Kxeatv99m G@?8F6î*V;ɰa 1L"[؋l맧ֳ( q .p]s?ⳑ t}A q{ FVg׷Y$q'3 A@ <LS #$L${4N&?| 3nJv22< ^Zec ą Y]>֘WW⌘~XW&dN=wҿ{Uʨ᳽N9~G+; ;ml!3-9N$iTe7Fo*g *7_Q&Lݨ,b5~3U6;Gylv!+/ ŊHG@FE@8( .-f+1[AW a2\vèP@@`HXo&C!"pRt" D,< ,s)7'B_.=Nki۾vvC7,dL% ƩߠHC.}4qF;XM@Fp~k=Y8BN1JP6ev}XNsycu_}<5gxߺM*H6,d0A ʀ 2EvdK:417ĵ|pv ea4~~՞@ ϟ?!dt  >x9E .qҎK;+5vDk  Y\\ksCy0N 7aM(yzceB{4,^hsWYBOw<8BU2ǒƔǔÕG>.X3&u)Ң\o]BSs5J+:B+?$ltVU8dӍ=Y4{ Impp6lPUsbI П@M,%g(C nq{?|kp+{G;5"kae=SQ6\jCED$7#u(4Ґm{L%#NX:Qx@bRӥzj1TD@J=p0 /1@#U2F/~":[}?|U~5+YxAd"r+RԾWB> 5ӭ_>RY'+ݩS)AHmppWW"6@p@y͔,eEtY ezc/X45t+ˇ=;]E96gZ"~-X<j8?F[G-Wn[jbt@_'0N ;+ƛBI3BqBY A!@P F{xs~?nW hDk  Dp7=8F `@po HT]>+{M> 5vÐ,8IsgSR9I_s:}@Cؽ&18Ϣ?nU_QȔ)>vJ+ 7Fon\Z{8pcپnZc<Ǒ y>n7Kr1?̂:i'^cQ 33GTn$Z{A4 yDJy9H {JPEB[́ f 6d͢Z;S@6'Ȍ晩j#jQ<@wބo 5@q) b ^X*Gp3MnD6.XTIW]v)D.X јZ>gZgqCScK7R:~s1B-A;-Q-4|F_* 󃙗R(W]iN(=],2TyN~\Dw G2bSUDEk7Ҽ<7h2;K ujtYxel9 .#&怀 Ȼ=.&5u z|\fzѓk< QwG A@%!yI <8.t] ^|@{\D;|wHrԬ(k ϱ7t%HUiIN̋}]HR$_C S" *_vCEET<Iф;\t|Wqt˜" +;?ONK})nd*J p\Za|vcئK`CܑA@^oRDJTF,#-X/}fF&33zziv`+ū{;Y;Zxg \Pq CeqvՍ;:›KY0}q4P]3/rU ٶ!HǑÝ_v"ٿG9"// Ϧ/b#;>)~`VύPv3"pjT̏R{NxUcB$-"s銵K,nO ܑ2O@\*/岰ݧNvdre\nY@ɐȕl&MNg8.EjnvzZes;bωsYiEi2=AĝP"x4  ZKw #󛎨RC@Hj~b~Vuǁ `9,ai:7j:.f7A/]>b,Wܵn;k (re G*]sJ5+{j .a]Y1Z5'"XJ2ꅈM:p+WL$_?͕EvqUY>}RVy:c L~nW +Jpgҁ@y[3f[ AJq>z:}!pe&W"WYLj U"§X,) D=S ±FÙHB%B\&hiݦeva8UxC\d{CQ)E°Bg(k,#I ۯb%]w**)1RNXPAE}zl!aCKo4"BFy940{4n ^h|A@/A&;N,jE p%9D:kٵx@V$3/=kt"%{yܰ"y w(jQ?^VPYq/T> db\7yc&rwK\H=s銵K7=fW>v 8-WhY@}BQZN.3$"VpH ;G:Z@Gܝ@ƃAiU~_}Ko`!dDXjDJ6?]h_[C c t,c(ji }`۠.#_sT8q?,`ghUw%lz%$ J( ^+ U (  Bf{}gYsfwޙ糟=9<'L͚HLϵsPנw}l9]lKupXc+g\AD[KrdWv9oJe-S.Ղg#A\a6NYaL ";en-SI^GmOܾ`f)=􍏟nZ~N81F6$@$񲲰^ρ`a @ pbZ `1eq.MQ Q {T,3a1  A@p=6p*3 -@܍zn"祼w0 o7 #Ab:%ӂ5uԈ Et2έmRiQ6=͋EuP[Ϣ'LΖ&Ǖk3מ:}pB\b[~kK{"~d"`dT62/!g03Z!:AM̌tB-&ԋ0-'#@9;8F#YJ8`F|D)?*Z8K>&0 iD>BTz#Zǀ.<&Ϗ?;+w/= 8JR0F8m#9 s)ts_'1YC F-ֺp\gTғ^Jw󛕉^Xߣ"Gh" ܑ@@ &k b %@Tݫ[i4&6akdT>Wt=ThHt\wׂRn<vu+P;*=ue`hX嫗Mz_@Mˀ&m՞;ۼB߬e`'M AA(5xT eAi&"jt  *l|iZ!85V7Ǜ|Y\3dCִɓn~/m?Fk:ĩ@"eG/CxF?y4}b/aұ Two;tG;#UF?5"ԫ>RTZga[PIn2L3ak녯AD<fyزǖ5I{~8$[[ k7$}ͣ_q>&wk.> {ovbm1 +=9;b0X'3DݎocXQhBn-O ~!NDHj|45O伔>&0:.Ѿ?7i_}lDH&&z;U_$#-JD3uC攃h ,[XI_D[=B-=A G t]8zeAӤ{ SEea^ĂKWOλ,<`4$cy!9h 6v]-* 8?ϛZ76 etGzaf+U X5@IDATђybӶ^D$@dfmoo j͗N>nkm/L(> ?_`d{yﺶ6\j*s1;*Ѧ>uOG C6y4T pOh!3GTؿZӓ0;vhn~ H Dp7B uA@ cq5 A,a[uuv"Ųd$1]}Vޠ>mB!-[8!iccQohŲ8#=D?h8xBذ</xvvbqQuƟWGOΨkմu_hLUI]OGPpʙ^.SA5w;*}#XR\`בQxAZgYS6vߤ55;WG7MX?7GNF%"'q"Hhh pFy ;UVI"~[D%⥑KHVD_  [k\wqC+otdľHf҈mŀ(^8xTu۲M7 GY8Afa_FB:0aI&g9K&xz-tGb{Λ,,@=3+TSw o+ }uI.?;@iP wVs A\x8TA@@ ܣ@# 〄* `@03"d*2}؅c:e/Z> l1y4.E&sDl]$\+lZ4]tp7rwSxw?aRIŎPE.|ȻZH銕3"O Ы9AJŹR &0q-7s V~-vK߼NU戲3؀'*R&}rso-z"A|G`n1aH@0 "4ӼL3#@PwռMst  Y[03V J"#G1Y]mVu0J-iweqB0$G)nTQYd)|xYܥ*,ޫD^O,? k ÆFt9QEo{Ԧnؚ9uV k7ӰY *Ԗ3=7#a!0^\ٹ )] `&L_BssLYzZ&UG,rGCBi@PP {d!1 Hp$ {`6΁zDp? 4I&vlQ;vm`8@Ν Gc"k$ƒ)dV)zLٟF0n":0c8NPڦ4 RJ% [ny"Zz<:F(?}SA,lRm5zR&4v ;nA*-Uc)]q fڶF INDc{KN=m_$[eñU‚m+ Y"w7UVl9ɞōlg\  Vu`7Dvj_@Mv;&nuT=%"ht  *! 8@(9 I\Fpxe;G&6F\Gk푿8 j1zurG\&M0MJO7gT|H*Niچ.(lPj5̓\0wLܾ;GUY ՙ.+ge}c{8٢bg(98?ұЙscchLm!"T(gucN[Nb*WEnK'& E(!>aH#C[S8|E@ ԛ[S<"*DgneG!&z0#hqhP yDK<7x}YJgV$ʦzbD6^-x,%6hѫǀuY3iɢ,DkEbC$#=D_29=؛"yO,rJi-z4|Ͻag,? ܒm^ i}vtIdWƎX4څdv9wh\m!z{=~@C;<Rڑ=u~ؗq1" fz|KT/m408a1 !:eDG  +AuLw87/zE#& ѝ% 2 <,  N6;@msշFo !L ǣ,l! -+4ף0-VIw>!ߧZYes}=^J )+h|sٙ3Rf}zGI-(˸h :ܒBܓ }n+ bը{Ms!V]Z~_QKO b[ZiT:'9CbD+AH1p꛻R<*"DnUh @@m $5b I͍V xF0Ή:67! [uEH!r, ]9k[E"OiGL%ӪSxrЗ>th8_ :#;H} RcjWbG'Dogs7ن$"GR*)0+|SV=7v΢ۡ]׬M},[ܚ~/; Rآ#PD()yg۫+y@ D-I] j!p FF@X9 Q%V}I`78KӈDwt2շ.j'fFwqt¬Em;V6 *M+`T|ΙE'Ζ[|\nO,J#?23%xS;<.qy* &[>-ⴁik=:{q>nzM}=rjҥݴWcX@w߼BjwV?zE;|8Zh060,wLMw'A##۰C$J%N{xuO!'h!󒹵Nƴ}4fdM+~+x.ְvK/miM-}c윣vij[&L' OӢESvDoH3&Zm;@UM-=1tScF1G`DR /GsPjVBDo$wԇS;S\>B?OC%gC#~'`CǨ j?ؿzI~J'zΖO ƒU U$"%[DFE+̭sEy4ZJF9z9YitJnnwm?&N (D8+8&]$כ9fVyl֘W,[~uGYZ}FN_Tر-wN&ܩQt/\6U >bA9S>#}H5]άq;4͚vL.ubH|;|]-n|gOޱeVq -m=Pp, +`p\ݱNF]}=ūf}=%zMp$- "[=ZlnqlZ@< pұu"n3d>{iF)"9$XJNFBaF^EҔR1An~)NR 8R&^*Jr\7a3q4W)U8+rsę'M$ҼݔaW>BL@'p/.Kgڴ~-#;o Sr>%vzֳ骹/k/ުuSou~}ݺ; .xszZp HcR*ZE}sWGp^#-)DG  +=q I p1։:ޘ+"fb]+2__|u'Wxmo=dGGi L9T!,:I~\Z4&70B?&b5/f$sL&!5/zM,qϼu-,@{,2N:d\Vo(BkFE40bZzx]5ה}7M7g ۈOdׂve&0([@斕 0H6;[lCz"[[=Zlnq5 @ cTi9 "^YuqUD`V~f(_,jڹThֺyBRY 41̫br|==BXw*k{ԮԪqttk>qYӗvd4+D^NFB亚@G,pG--wl y%4ƸM[[^ ;[ح/<|[E!E׶ʯ GSZ)UǮ:A*F>8GӴoNz#Ui?\ayHy|E'I lSmS8\D Dt|d"G8ϧ~a;Եdsڑ%{U=􉅩72 #Qg Tk~ Զ9 v2kbLw J@bPQc0c[+-6"Z=sH I!gsXu)+&3ׄXQ&V-gTEyם8ewf` EQ0=Mݽ}S);SGX8>whyrjj"6LA Nw4ϧMM5sO)@/Nsdm,pC:t?"kN 1vk ʑc . [c%/QJ%ܽkC~Q)Pj[}Nwhl .Y( %-dlX6!Bܭ[Iܭc@@O $5ډ I͍V 8/[g)"a0OH b(M"r]JOa[ˇIdvx]N/:U*F>{(Rگ˟UwXҔSbGs>hc]H,m8A\b8"g=q<5,Ÿt5P|;Y<ڮ+BZ5)oz7jgO1y9A A ѣ?iegg)~8Ѓ@2 Abe=6# D %G㑜hh}[ƣ֍@@FBRs+օ4%ZMLq#80J|S"GDOS*Z:<ziOmz,-U"C$o h=_4b @Yq6} )YϜ"T!Zᾓs-OoM9I*̎@~O5i8E.j 2!E D|-(ojxzW.xwҧ{R$vjC@$`$6I͏IA pbv T`r- D = ! O8bʼĸfx+]O"BGGN$m_舑 D(f%T b4P,G.OE٢ZâYl\P^u,8Ѿiθ<\|\)˧Qr{k 1:1sI P3h`>Z[m_+gҏ>w.eexG˯`>ߩO\M,q^4l5+M" DxBN"\t ^'I݆E~aC bH@wVwX'Iͭv8w7:F+yA` G5GeNz43SDزG/n k䛶r{fӧ޻H4xp^1]lhqXn%-EsjU}sM_J.*F'pGw :{q~퇁,Y*6V~!%Fm9yOIskv'NImG+yokǍ"cGx#i[_m~cX{b灆^jt0>% p#:@RsK@C;KjyɬI9 %v>)ъq3͂@vv'L,F/qJZaТ~~6s~ Q;!NRzN[P)O͚ H%@Յ_G QC$jZ"AB*7$@wWw0*DpnGF5a=x@ wS+jEdS"[d+K9:KQiIT%*E-{ ,L&䤊|b %A8:*;oHiDEQ"wҁ0jNi 21q_PKwɎUU*t~z\t4JCSi!%N!M;EpAT:MwckqJ^Q<+ |[/])G]%Co;l9anw @v-f)!G` ]UnFlX}uv6!]D ֱDO  ' ͬ͌6 p5VFp F|6hn;FK p\\AYwѦ (yRR]{pNk(qdY5sv7袷gHq/e+ymEoJ}Dt+ .l&DK#؇zC(ep|]5זzt4E@(8@$m`R6$\=@ Y0!ȑoFhTH   ` {z;V Jx42;ʔ gH?zpͱq6ph~;iDܣ%2^;F ]Y5o`a>TۢF zW~%*]H/YGk77&Iv,ͥUsSA]s8Lu/|­Kw=Slޫ!Y@rP\&~sԽ؝ƶmyMk  !a$>PM5 @C$@ p7E.F/n_&3Z{+֮pDm  F8g /%ud=+"gi Y1tH(r%jE]P^||¤\\;%>?@CౙI: pO:e &8PQS җ0vTwRgwy!Kx0]bQ"x 7u,q5 @ pұu8/>,k~549:I];=-yl\"`A n_Dr@Klh``h6L|qs3hRҪYw^(CzE l q.~+9iߢkv"wu*{Dho%ng#w&vj_NUw)`Jt Ec3ر [cxYK!c*nXM{ ٛ")lb#+D-b($\-͍V }8/[c+KKiRhFH}zD햽42"@]w.eǢKtz7#\sK]$oOAXv/LY  GLR7JC1J:~D=w뢷۫"gEt TЂ2uJr#ŽSiy!7[R!--4qC+KP:U2=49k *G"sx_c^9^nIx8(/);r>Mm}vK6j~U;!q iFU)E'rr$kF["s!UEn诺teSuU\QicHUqL&u-ݲ #J %2 _qA&u,U,U[}=XxM],P> Zz3鮏>vowx#7߻F;I%?ZB(F U4.Iϋ6'}  y{~1AH>@8>o}[ն\A DgEXUc6 Zlnp5 @ 8/C:VDp^ˊs Y0Ѷ/*.?s&2X/o&ުőp.\Mv8:,/K7_(Y{hs8-z;; Vȑʼnz@w:)z˦6STR%!c>p{D8 v5!}=N;/w? %x7cxSJl|rv} )"ȆԏAFw(6C lÐ/ol41G @b-PD-Z@@L o!$E׾'oʋMQܳ]MiQKe +t"FdZ'2WOEi2+#>tWϩ4v`gwRFL=PKDz9洰&ggOg;o@]*RwΊ8NuS>8@8*%z 0agٓiΔh|0ܽã8wwnWgΠǞdj4y?ʽi[}Xphen?̫8<ۓhvYs*:|R6"s ax@Qs#ɼ썵#,N"hL֮UkBo  N&/jͭ渡Cx([ǪGp743mlݡG>!N (&eGWiQO}:/TtDGw_v_C/Yyzi/hB/-ʦ\(6bQ#6R%LM]g ϞEiDmaseB\-ltHOޱ8*Jpo d-=QJ#D?%>uD̓WЏ~I{8(5& @cmq^ ::V @@ eCl$aN`aE.' hZ R /s y7LGg ['Zȿ'r<5{t f wbM>t%{d7w5 wvQ2wG7?&\<Xu8>@Fui" uVĚ=-d]XSt ,OA sJIAc#0y|>zb{l{ "A 6wpѯ_D*SuC7}Ӭ( 99YaLh=TWMT 5pP ?y?.j8lt)'h&c%EbE57xC0NGױٵܤ}hhR.ze%GKnԏb$]ϙM7c#ȿ}OnӞO҃TA}J'Y$: zqXX K9W.|oJGFtPÉ·y,i u˲DKTN^]hI4?.;*G`' ag?ôQXLꇗQrzR>稻wHjNF8z;&L7 Pvy)<0Dwp2D@Ep,J0m֮UkBo  N&k =9rh BblFjXA\4!ϋ: *BOh#+Q7ETs eF/Dn\؁ѿ՟=Fm\mJ! `zH*珨pktn}eF%pѤeY&6:gpRa^&Z'xѿcq8Eo5jW3Bd'.c\9w-X JS7#|ro)Fh -^n(5v^J7Kgr,+?Y9*y:K*xKjO;SOiXL@x t m`w 6!L:kW/o@@@~ @w&gQQ*gWۍ] 뤴l'Х"5G%2?*7f6jNqDRYi9枾a_~m ehbqM)C|VN89H+4./@FIV}w֠*ਫdƈrm Տnܣ"nWnK1qן<5ph ,-_اs)ǀ4Χ;4jw-2EnH |ؾ9Lq΍M|ყг\IVaBx!vRHwnGW\r&77CHS# DIAJ1uۛR<"s3]=@@*[%$57:Z Z,P^cnǷDz7pnF=Ajj=<ל;U׶6yq%Bc,㉸WoWbФPXyzĢc//+JV˼ՇgPqx}En-QJ>h~ZDy[vGKhNy"M@]|e̛VB~옻Sƚιw6z:<:}A/*,=.zYR; eAe |+jA`]uP@";pw3lZ5(c vA#ҷ@@x'MܞvT')wjo)ݿz=EV{i 'v%T.7~UĻx1W|4(z3OY:)-EmigҽRReXw<΍:խuaZ`xWT|56JԾ6LMj\G\KW̠~ptJOKavif/32gGhB99]Lh@W͋%{bBj DՌ1`޺բЍ :+lZS p74LuF 󒹃JǛ &p܌bRT48 'P+N2+v XH$LҢloxbnkb3}aU&vz4,׈U:1ל;;Z6#v-%Z49D,uJPYJ/NX;ʊ+vRq~&+REI˾7UBrrv6Wt0+dN#ϟ.;PvG`DxPRV-WAO} ͚\ACXWbt޷"h EwQ5YoCbʡl[}֪>֩h;BC&YB>p%-?aBr-*HvTH `5nͅ߿D_&G}dT-7H9y6,Yn-O ~ !U57x!J։%R<3d_˖%%S?'+M,tѤđ>yT|E7I"жEl2. SE4#o]JYl.G\HVjl"q4P즶>ĉpB9m(3uHs@~43n L]d|i8~Y*ᅡW< 1w9|B|ł>=ӄ:2="nTlW+O~>Fz@wN:ކ'"Euٚ\p:g-s:5  fRMZmOυnY)j0t  X[qC+tlѰu_}cpTTQVJSG$>~ qlh꒣I_r&k&i~f;}۱,q,?@s+UxGz6^p my9t{;sshќrmޤɫNM5ۧM(`qvqb]b l$ѰRq"۸4:#=Dv1=}tf#a$kuO]FW~t5]<?h}aG_@rrG(P(p0F>5kVvh/oc0U'hT1{H]XJ wdf3oy1=yetiNhHG6_wdQ}%-#J3ͲQo]Y.#A&eh@' n[c:Y6n-O ~ kU2R9 KͭwȍpT {t[]Nib \@O-YJs`7OJ^9 UD~h2J>H/vtjQE(%ѳn~ ]uά} 4<"1yسGmל7Y2n]5~Yq6M,j3QQ!3tK=4w-WcagbU\I~u-}i;icZ3&{g_׵@ :DOޠSo,@@:[=M @zϬ=HR t ^1^fŸh  6[1\sqC+otl]';H9q4NqclE}wܼH.%]ieJ^4%E AM-?.Oy 1빽.z/;isE^?3y9cbHq(J`qQ9_,BMwDҵiY\=l }sᅴ~Ula%;eлΞI|>G 餏|o_-} IUa2yju>G$$*#agh5P]S'M׋݋l4']M@@R%X\1H5<##nlH3Z>-9QV#ij:萔.ɥbM)}$ȷݽC;BIm2ӕ=Vt'CT3>HGI @;}{{oYj?rdhǥ+fp/"~ H6Sʓ(]E$F2F`vEm5vJ}pԁ) ܠEn(M#ꔱN ui.=9YiX9՜6N?N]F,rG,=2R["|+BtpV4G$`F>@lf  "oDXb%մCn IvQk <@^H eq@xF8GX}^h2{ eU&K<thяp:pP X^Cs7r S U K/I[dAo0_ϩ 4e4@vNzO!;Z' 86AcB1q_~~77y@ݢEmO&zԓ2o6&Ht   ("#Jb!G cpub㍹%4<ϔ!=ٲMLg@*IA}K8Lٸ]=-;|t=w25S{@~hj%XN.\?zV)ao~nơf}=t۞]*YuΧWƉ[^OR >LwJC%9:c׋ކ13RQ (ArQzPNX"9S~<79Mk?'Ʊ{Oc9p";YF"B&m~#;bA1&adh HxȅqE nѕ :KjZS +!ݾN5Fp͵><8IeZL,5tpT3Ct܏K/V+ݓ_?HZnbQSC*qWU$hkzM_y@>+5TC3(NɶmK{c @j']s+^s+g,7@!p Ʀ̑`n G7 1<ɓ i'Bhv-2$ܑ7ق9 RW]jǛM' q`UXrE-@@KbRK3.1ZMF2N`TxoDUf` Fp^jn4}wo3@"3N7$|w~^CGZͯ6bneB""7Sms\%t#n祿Ws_J Q 'piSƭ=Wl pD}MqAov,A^@e|/<sDP{\c pT$"gA@ pH im #cHڕB4Yky7 ܍4ƙKN:ި8ZgSFF(48\ & ]tqR}B@/p[^%*(?7C࣒οRW8kɀ^'TsU!84N  p$Pb#j!iQC:Js a)ՙah`NJGm>ʳOPύ=m/eQqJnQC<ƌ PQĚbf$8ݿ-;9S:kN,ˣ9SJ66H0/SsHd?aWۿ>&ЫvWk͛{a RN 11["8@B`E {NEaHͩ#3ZTaIa0 @@c&CHj ox9l,Q:-N ȏ$I L;(4e8>*ihC}sTg$lD"[_y;~( R~,6vaR\WE)N&'H൭f]Keax76S9K'{zT%OjUw`DEӗRqAT|Fo'Ok d]8}p=X7 VCU P;ta`x@^<\6gֺbJAW, 8@(GqF>#cv2KLI^ʞAg3c.x.{f n\>9141kRe-Sx%?Us^`Ĉ^UfV΅SPyNİVj ʦ :Ht%67֯ӟ_ٯ5}jiJ?H jWEhúu?՜1Y$1 9rzͽTH`5Z @BBR+[)ٸh%—FW7f"݃IO ܍5ޫϑZNCQp GoG;OWOW RPs[qI#M fcc`BQ5אIV&N牂 eҊ' jd' ,%#J੗,?@UŚBmkyih7Z&  [,*(@0M%$wx#Dpv|$0JwĒ)ٸh%MFrsFoagԒxA=hPBUCr)I.ufsK=]b1R/{ C}u?oL1u=Z'6o5z!ӕ ;3^W)6]:M:(ϲdt `/M{Z?{QkDrUv2(t:6a6pǐ Jc ` aJ[0*tDpvZ  @Hj|Up5 -@P8/M(3'pI',֪c{@}K84D8C LO Ҳ#/GvAD%NizuZ(t=5yLT\ B]/#pӨ4WQE4I+Wl Ά0"@E@ "_ / 8ǎ)Z !0OR"{R s7|{A B&ytKQ#G v!jB  D_F%ڀ exN0x0}pظ;e|ax(zN},p׭+߭֩YbϞJ;;g({8Az :q _ە MpQvF= ;.SG';Zp|vMW9j!%[7GI!KTA\H`Py~ο9nY"w$@KE]FcںuS,A a  EzqRNӋ멗C' Q( ]@|J #"k!\  0d&fDRuÓ:,RYpTmG: <"8V8Q*E~c[qq){Z)r;ħ0@9s>X/O6i']U$%z4Ңlb@$k !vzPK-a{ukX+K'-U %kMZ gU)/K6F!XD`o}E= 0T0FE M AA @aذ#0> ($ ^.Xu @@ÙQbD ?,?C5q^HѸCC{P#D(5Bt4p= [hi_Zќ9b9;9~ 0L>׷5y(}^nR9F)uiΙKU^[yljDHPaM(w[{Ou!]B\>.%@P='9c%q{d%YeҮ|3r9GgOHZi睶()&#wjīh@@ {C>"ZS!I +3Gfھٷ0׎>c@n g%J^|$w3 @P/%v@@@ŭaLFd6<8+KdKsTfCI2WО o@y@((4,S:Yʮzj]tUL+tFnj,q6EY@yv?|%;!z ;b+[?wBJobG{(FێEUZZyχiUP@bVmi[$ze{MD xS6G kG1pa g%C`kwt;*-[+F6@H~̒%$Uo j3 ฤ~\#IZͩL[(c!1|q&r\CW,TLOq 8:f4f}d9>":r4@ o[ X]xCccV61}iL #\¢#)#W$8ʣ(|bz暈3j2(&p6+:F,R>V02B32ϰ}o1NwÑCpynkwvo,Dpw8 6@@\As$szj ฤթɺ&p L2|mE>[-W[ۍmD,ɍ5>򷏉gr,+11}K =Te,E !p|m_~+Qx{ Y]s Zg^@00A Rc+l22A ILCQ Gg Nxq `,G6)zVni8`EAϾҬ!~tE}"Ӌ=?^c$#s|Be˞(am?1d/_N}-\Ec!@}'+pP & L#IXpwsУWp>=pPa7hwFpNqlBHY7h@DՏ,P@l8.M$$NvR/pˢ'2j:sd'\>,z٥sh?ꈰ..5! IFE+,w2'IBV]i1 ?Saqv퍣e>3K)6~;͏E=kr$[;2O?{*iG|Rn&r $3!jҶu)-6*)ȴ0@@-'4ү%VanRKOE/ َM  ;?BНH2O6& 4/ wmF }@@-܊kHE18V89ޮhqA+%JRGYuDXG!E,( mѲw H%Š`. Ӆ ʅy0_y7J;:wEMX_fme~ma1ճ=Ka3O{>L[}v6p]Gӷ^'(7C [ HN<48_up@BpEb) t9& ˻:MZ+HB;}5p  (P @t~: e#IaM$-4;ӌdcac"&l< }n Xa$W+O-9tzgW% j^&t2LtSݵH?>zb]Iu_/G-qwH&p_7:t]hDZuh9_ 1@G1Uv.>US` @ /G.j42@2ZK#5ݡC @ي@m.P,V }C}9'SԢh @@Jp>61;i*j|yԋJaRTLP:p@ߋ{q,VL;ۅMf{RHG]F:~Q[J8dt]x-BD"shNme|ylLq{-+ǵ'JۘU<:{Di@7fZGm0 H>}1(1K9?gKD#PKx#mk>ݰ @٦A6v[~ w[N{!\щfIVWh @\CwmC6nJฤҙeQu"3k'^::>u4(NlrSO0}?6w*slyt_H ݀X_]K?[MuI)Pm3RG#d=s 9%IApxc|&z :|vC+}ZD8*x o7h @@a $6Xj>^ )tvStysjܧ68d!g?C )0PS\.PDx&>O]7F5jK m) *|~c>u0k[v^Vn)[ewELԿ.^R5(  `Xs5 RgUބ.i# Ps dPf&0c  ^w9H =^݄8 ܭԥ'U+k["SYDTWQDMm0t?OLI"iH=.uWhmkY!09wpwk К_vI,<\GѝlWXTsD4ts1681j7a/ @n1 `;1/lveZÛd!E)^0ZFBhg5  `kfiZ K )KNUyPd"wMKEomiiuJ?:. h,#M1$4-"EEpG>COǪ@IDATKN:"eܑIO;f/A}-{hf|{.k.Ao=EǹBS@4nOJXv LskzC`aOL~1.  h;zxX@em5Y7Ӛ:*`fՖ8Ixh;e0$ Z FO#& *V<yUwk)3.a="+7 HMUDe;{GztIf*Ӥ4>[S&W笓D<܅;M2A\D|r쉟~zEB~)^Cu'*+̙^H-,wzNBԮ ;lI_*^aH `OYl?<[vzX  ;g, ؊@,Y>g+b{:j"炭  rr-$ >) dO9]3Um/~h0,h }`@M9 Oˎ!l䘃VUhl,? m!FMJ27;*M'@ui}Υ|\Gu=@7q{2;.`?tr*J 6AY4gB8a*Ts)=l2)#5tNA\Ow"uH1>n%,b8 {(7"!hl@@\@wml!6P A%m$&Bڍi !i5OhiO0~ r}Y6^2rJ"6V~_H֗8O87j ]u>h |.?fD#LM{o?=Јb<㦕簟܉oE.ϗr^M@,hCvӆ7FkM~8\}ϱ˿̞ ՙg;{^Z8ܝ5mn{" `NTU "Lě*&02'#IMIKJD<! bf ;)$W` 9bxvz74 *as3[D< ܑG$tޏ{bYxTv7Me=-gS Zt6s?4&^W<睟N#ӑRRR+HT7vFyc @6X,RKm v@T Dujˇ#Zp;DJֶDQmQ @ !"[ +:ye::F\G" K."耘So!Z]+t`l"?#O@=-'>M†W 蜘!3๵ENt>M@FZ հhH  L}c/'.rB$p̬L Gݤ<ؼ7]&nA\NO\>plCKؘ6T/ll5otl>c@@M $6ͥQ @u8.B#VWeө\%#Z9L上@@An]A6wmZ+oO>-[%5ŗN\D5wtk6(vDK  "mq @@ ܕP\F&Λ\Zۖizs_9IL/rAM ޲4J.2瞛#ӱo1opck=hܾ0@w6L4]MV\ %|x۬PGd:A;  #!!Ŵ5Z .xmxctw etw]"acX}y4$nR.qș*^8HMfj h6(/C   'p$J|5m)m Cs+RLȴtj;Yx,@ pN `&…I#eI=[ ~#~,F[&"j")}uFV:6)H?uyd ah I0S<*9Oͬɟb먮'8vailB-2b3ks 1G?icFs3<4۝{jLHM ^0m|[|fic7 AbV6ɶ=7 %pTfAL@dm;BRm؟ojz1Dƕy+{ !'#W:+)V]a ;-B^#R{l7 ۨ-W: nՔ @@"Eq=ꈴt-*0)'dړ@߈͞4]v. $K*\[^>D-'\QJJ\ČP@@`2'3Q!;PHoj̀QLW@P,Rkp q"rOa&Fҗ@1=ֺt{dy,@!pЏ+m0#haRrB] xP߮AnQ `kx?uw70/ICI=[3 :~!Џ%Z7Th#6nJ`J5f6ȸZ,0D؄,ܓ]u\T߿J(ճnHcb/!r@@W'JSj/e,LGhHsCk =@|炭 Hz:8n>~|t0ˉ  * J`7JH:{1azFEp5{PJ+{-аlJ@6g?3gS]\) B43F禅38?Cu?m z('Cv j`X" ߼smv^"|,LGHH>Z96Z.+l+ 4+8D+dvz'o< GE6"oVx KÃi΢ql(bZG9g@lIO l940\E=&srM-A ~,F(!6P MoQǥ4*/Vmd $= &rRIX:.T'MW3.P_K%9wi[ a_7q:^D&8Iw  KB#^E'.ܽ.C؇y$PIb^+.aik@Nq u<.ͭ-Tg+V6LUc+.>Ώi dO+ t w,xIQQv ㇆B’hŒI1B8) Z5N** Ts[ s#Do7}`ܱ#XZY#x}cDpoyF׏'Z@wm#mTdm֡؛KҜܫ@PLlF@Oy)%}J%w=' #'QϠw&!K'D!IR/qq[RX<'s݂~$?0N~g~KY.:K 23ҩw ZXl5 U n#Enq %кh?3Ϣ9ެ#|3BO1 @@TXӊBXy | ฤ (0nT6BDp׏[JEE"gf.LB*' rqF Fǯ+D!^A74 PI/h ֟ pt -ELH &)ig x׻ּ3A7Xn毅יj;bmn\4`D!8"%k(fkZ "q*%@H݄\${HEQ@A3d a eUKh/Y, c!gtK*s1]M*5dFDuaɤ Dp.H@w`>gi^iT/f<,LWYDU9yy]ͪj0$IfA4fjGN?tУ)aΨ )jr  `#&lฤӤBFfO/ |m6>)+{e>>(:Tf87cDtL VΝ^HiLA'Y8U4|OOe,?HT{(;]v`5,)MOvh@@:{G#7>Cۮ5>W`:6_禇bnYoX`z8#7ކ΍#'m#E-zڍ@@#fG@@@SɮT] &t-@VN|(Ss";x>%&!MŒ!m@/M6-\ k?#9iwN{>gmP7S۳prBs dȸsg57;Hfx(؋@{if񀽬vkw;x"%LhT#Cp="k0L6TE<$P9*BFWT˟R( ]F@3pq{pܝQϙ]*1UX a?g 6'Mgv1VӋ,;  jms,d_$p+'ֻ~{">Dp78] x#>z.]u.Q_Zp BR-cidd-ؙ@״h15QS$!M@T-iw"f\es.G̑Ep׳'5I'Z]aAO6ߞ= GwkXr(ρ] @@`~;{G&坾{yv LOE7Jm-/ٔ*'zf DޘgEG#tBIh@@ p.6F IYZ `O1,Ki>.;PKEhGwM+ {,zM ,`yI vn% ./v2Ҳ:}mj 7*&(g#} @@$H/}R|Ut-f4\G #~m!0[7r2gx ! p7qv1wtm%-{ oOh @@)hi]e>eq ?Q9身]N݋>j3m)l8CYaEe.3>tHH-߾, gfxhQdΓg%Zk΢pX|a-kh:+ `cx`:8@ۺإj}O7L8E]SlڕҕA@`J0L>ڬD-'ܵofO+F͖yrK<-LZ/3#~XA GF>>tO8GŽϏ[͏ͽQaT|d$X(L?&a'PϢ# ^9  ' >gIg2q{RpŮ!||kpV80@=;Ewᕽ]?0wtk2F~ sڈH>Ynڗ,Rό0:"W,R1^h<,+6 lM@;c+&s܅˅;[笌J6 .Գj_^jvW]! | wd0ҿ%8ZVKYɧ n#96-/ܶFC@@x=4*|=GMEe%mE_p.F\mP K e̘:p! D۩1y3Z-~Q`9| d"DB{^\'hTŪ†;L‡;ueѴ">z- D!@\KW?oYIAqWyJmbWH>ׂbfY~Aw\, `&xh5_L\m..BT!j  `4DzF\mP 5J˘q\;][`QܕTzJ1:4;beB7 ɱ_;Bl?do||DoOwMhu%"{ێS@@t{]j 'X4C* n?>'KP !%&**ܧJAt'oOF70TDUM g+j,K'{iYkҀe  J`JDx(H$DF֌Lt)!",{么@nV0B6VʐEIVr"I|–)2xҶbF3wΕ5$ز\(!7CeLp-(뱗N ̞w %zVP9L)t W>4qPKwP@ un"08G{ڊDXLC߮CxBHbR&l "L mn#A-)\}}C ~?d p+ HkFwgS}MRsL5j?D]boRJII1떛}UR(N}i0@\N`[/ПٟDUf3q{’(#euiH؈3Ɖ:l.WF~K=Klp ;KI|E}"cI 0"׭¾ܹb2q`F0ozϳu%8&&0wAHͤAdpvAim{6w|e]NCTƔDؘ26xaDpF@ 1(iCS¬ɮskX#ćܕq)Dpq[NI~qLJR[]<՗ I]HŠ ?-kAH-jp3#L7c V nVXڰ`[]w;fDm  `4G pF@ 1DpO(^ ֫sGwp3ۓ]L]9nf BS VXaե9tB ?(9_$oR!qmjTJ\ C` ܿuNʋ ,:>]3V BzF    q$s<1&DuEK}y5p.@ROwP5RRg3ۢY%g?0FA2{ωPܝ3KjvSZLum%Ğ[w(a&nUsi1~$Qe/o{DQ @.b ,2(JR|%Qwl/_!qVMJxId5_D/nnF>lg${:|}iL#!J:G  f$h]eHrWh>` b @TQ_/0LQZ @+$h̺X4X~S/:4J# Ʉ#a_S}+/3KhOcoVy|'bt"ˇoHo||a>2K`V?T塢l@T-e+] @~K`4XWwSL|0OD{2{]~޼rӌ59@>WLLHh^WzI6yv- uOD|N/S󹁉?L(5ȒtL윅d<>AGmvzIL@vFw18䀀JZdnz#Ɖ ML?tGicWΦ;IK{l=MʼnUNm-2" @ZZ*546c4ј ''ꙡ=mpsW붷}C' eÈ: | D"jaj۪%;?+4SٹsPY#(h<*3=mcVJ?lKMXtQE(&;e;`W4S>^}I=]wռS>:.>gW{( ">Rv%:zGh;]?OODbiH!vA?L6Ӯ {xwU$n+r5° 3&A`mx<\n| 6!M f[ x/1Y,$3QJ׏ccFt@  EBD( `&5w 7ifq3GQEbm]7%pM2fN X#zi3=1h[4[mP_@4ܚkj@@@1ŒA*nq.eQ<nbvK4 n!"'0hԷ D)tJ?91`LmGƔy)܉(:Դ\,dX,{[7"*(8Կq6tG +G֧/ 儍'H/=aaG9*a!2dX @@0[ҥ_x~. [.etG -М 3G܊ @NN6;/hIb^ :$ ୺ <^M؇W5;ND՗QE4D@`< ("`P[-d&*MWMFi IݦUwh @@QO> bdQQ @!1  P )2J[ۨPp|*mL͎ڨ7h @@(ஞ8Vꡡ($a"'`"ǔ 3%;C (M~W07-.Kmt&։]&W-nģGԶuR*n_nR[jw㏴o-tk !dd3uaŸrDX}{ҍ~',7K˽1E??/9{]}cv{YA>h{{6Qi>'@#֧_hvD 4-=<0@c~Dk8?Kkv2S4/ZE-z@@"v\-Trj<(&kZŨ(&*ug%Q]  *].UyBGC5a0\&wߗI+rq8vLJ+.JKb];B}9OSK<;~Rp_ђz>CC 3'&1`&rTZ]Њ pw Z0"'=.lo 5@N'@z:촠X A&gхLIŦQ|g# YD7E/=>Ţ;ܕs 9ю蹸?z7fK0v7vǣ o+gzH]Srrć4iLl.>jfΟ"]c) ztMnj4}% ί(1C*1I,C]L =#\CpuFAlDw L`A.!p7q'xxQ *|HeZ@RQ.UBQ |ƾ{ce@B C"&`{E3 ܹ~Q3%] UB]R L$&]D\:C\匆vFw /,|4(0$ 81[IXn;gSHA, Z@pZIJvһ8m?أ9DM.QeɬąO~򋗨h, tq/n{QQTbȩA@HϏgB8dxb^]D ̀$@z XS8>688CΫrێkCŁX8@ x<ު($K!3Y^`̒3Rff g'M.zIw=l2ˎCʄkv* wm4ʖxqs)}wkfz :44xLjnq7KʒbnlE]VEا}_}G7Y &(nL)-3/QcO,~G;}uK)\dy!}OA1v OTxIʑfP@@B.%%l1fA#+oX%$hشl=+|5ݠy M{KJ!b<\nvK4 n"UԏvEpO,Q.jE"vi$FpnG`?H 0HaXPVX?'}' .pWK*%TDt}8pSp. pa޴"RE롡5wbҟB(fcP~׭t O*΢j/eBa+O. YTw%Gp{^+%PxJ6e"q;#/Cn#CQeN #)Kn@@`dc1zי~- fz@@ 68jZ ERM Ǥ )#M"I j"7 !0 H~]IZDn m}ـW,ofJ&dQܑ@w ZNœުMQA2ḙ*/. 'm_c -+y8$k82@y܏Wб!E%L>CkB `i!\<@܅}݃('rV(  `Q_ Z RS[kwDpu( j!4  `Z@GR$0U˰y8^05P\ԨD̘.nzwC89!s Ew!Keě;Adr2So?z͗jp@hGh)Ȃ%hXnftͥ3[Ci JH t儰)H,lxOB !0N?o#]?OQnE3J7ˢ?Jk)6=!V ("psTQ9JHRh@Boԓ$@KvZ>ML`ąP:> ZtuBܵa]7DGLP%2Vxf&ZJ礋]w'V:_w{Lг"*tzKϫVTGs"sE݈ ۷6Gk0H棟}W#O{v!iĈz~\ނqO66Ӌ[riU, )*g'.nG.=; H_5H"="/_PTFG(T,z=w`_ |=Kꉯz#40Y!%M bUPJ #Q@@O$0 @@ZV)[UE֍}F]_[ DGj IQ 55"JaQCyE5 %.Jf"bɥw7ҥVOW(Ϡ<:94o.(EF.x85+a7-o_M[w+cOEH%a3e4iTvϳbX_;>*Si3LEoYe|6*!Hwv@n@ G?ⓟ$}],᝘lƢ h-QvUB2[tWcWabyT_bo+aGl/=l?I ##,Q=cV@@ WP@4kBVf:^: 6&Ew]G?F pWKd"kZ !"i#`ɮL:gZj'xB%՜ q\,PՀhH;48}GijC2>"Ǩ} FM,(5D/J1ca6Dgы|vjwc;uA,r{lQlcVd*IT}8C]IS>tR/-qG` j}T[XiTҿpZv>vl |OF- `8_j[ F㙣 @A1N@,=ڌ "nW'u.WFOi20a palN/8o3)$WO]@z<V2HaS)ƅ_]C_޲rbhz;, qomACNL@SAh^J"j<E bv뷶aӲKii u@@@36Џ@JY*シcaUmIW_bh*}<@Z*B -Xh/g/V'Epߍ;›U];U!"KdJ3D%/_B_9t?cL`ȅW_2n|=v}F892[u% Q$H 3&HkҵoD 4C-{^ E~Qyl<:IO*ɎӋhbr``Giv;Ya.nFO ϛ[6!OԎe_X@G{I΅\!>)H !J1ȅ( ݆  . 4Uwe_̞ɍ['61\+T@X= d%B}La @@_1,]KN;)':hFR-XgnmUQsg``<{UT&Vz]Aih;zG?@eEYVv6Mȥ;n9t;ٗJz:ƣɿ-u4J^8H;^D|8 QĄ'T[ZGzf+9)4su1b5ogo ",y`aȗ.(%tRqPabv&hgv~NMR^#ZZt(Hb}e4H3uA@`  p7w T%#ڕB0@G8;d?|Q A@w %y&O:`sãK6ZH& 7H b$m‘pm-./]G^qM|@9Z {|0.C d]qh 7}۬bѦ}]b5[h [AC).QLX:LE@)lGH\4(J5E^|ݷgzי}Rb`]bR6c̈́X2KylGRO_N+P ~EudQOsٻ8.z#@, "QfYeɖ#&VKĎT;.׉زs;%[ű%R({{{(V ffgwb)vwΠ_qžY*}Ԓw_]U"=Ӣnh;/ f3QTx*c~LAɾ}lp{I-#C  3@+Owz59G[p%] ZI]n۽!C@D{Rk dpw+=T|w}]g3+e0y`NAfWͯeҠ25&+*ZeYxW@$]߰ٻPr9%rxF!ii~iV546mJL\,&>YeqQR謽Ϯ:qKSՙ?~Q/괁}Jjw6y.6# SD@D .U?or (lqu1_*+J U-@XwEc@  vm]RV9 ` G^{/h4OKƨb#MnTv M䞧w%i%=)6'Ж-5(UE`kZt6SǿDzO@*/֚vloz; ,ܟYqXiJ[{uȗUvJщ/UYu06}Z80R~t%[(qu?4XRKr[Mn˞,pKb'jL@ pۮةFƜY `(C%C vx-&qݽ*bR1 ӷy4ah䦋 ǪyXSc:K%+:k4) n/!ǚN-S}%} kSJuJø= 06>M>g-W9g`b*}ٌ\Qg^\+!OKY/VG冋s9QhEgIQ 77Wv 𒬼 +8)@7^!EJuh9, KԦVL%]OjC"@&37BL@g8& wVj3+M<Ʉo/&3ة%P<-݌uF؎. m;nXXQ#2],Fb;i8X)oįu*Xgpd^@/*QjޫޯS N}N.^dL}"(*.Q=H"BZe@ u>Iݐ@ ݡJ_X}\:B'ݝNaFA@@H-07!' ?N2;5epX~w}JVc[w'Tz1|1&xbGIm?4~ (?f_1 WE0j~$%ٞ0ߓBwX]t,us^?.Nz< h!=3҆D# W}0It/K͏`o:qٲ)MPQ}⚀Z\!M h̍0K5;n3v/X~,%:1C$^!FtfSt(+2Μ9274x _ <Y2X?R+dPq(iO6.9 U> pLxV,ޯjk&Gd /{`.Ru/]M_g:H*Cc^ s;z999*˾[+og ]`ȋyy7K8\g$xo [Q+;Eclȳ; [PR\?@y%?~`(IT8 @wf HE/7ٰF;b%&$MFճ= M^Wc8x,'#1D[ hi  l*pzJ^l3b_,a8Y|a-5v_ 'ѨD_B܉ ~v(g > D2 .IĸN)0@{^ j=BP {^Hbg-1qn*فV"“mNwBęUЫQYw#nOpNFo ObGgGKߗX_h 軮W]5,O o! ycnTVni=Gۍv[ -gX&߸{X~fVA:l!Np0 6JKK8f u[ !{ۦԇ =;. 4sjΆǤ6ȲvhwWqODe A%$'!`({8C;fhpE 9Agu6*;InP)Wϯ2yCG\Fm&Dm2GKcNQ OdGtƒL{h #/}iYcq1iqϭލ%j@ \?ǂ\¼4:=w5k Ǯ>J*ȝMTk RS[>͎+|$~<%q)(;r"KW,*;\-? wɑB_=7 "À]}p0{5gxm0ƨ@ l|*gX@}\C{Y[ cb^h{DȖܝ 9;@ @ܓ'ɔIv1~l@,kw6|OMKDwBHoȓ9"䖯jh |ubv9*^\FY*=n'Fmxb?,76XcZQWe|T! @YY@Be[K9#@w悞 @%򦪂):r+Jp*Upni{^ hqotԄ .2;#g!` 5hV*pɋeBYbb)и$u͎&/rЧ, _`+TFwC,,5 izܳ<=4sW7j GQYƿ'#j1дIī_7v |ZfQ/-ĵssdY-hq @&_e}u78Ay" @`pT28aIr$'04ֲ֖~KCwkX"$Ax aP]eq%*B@Kǀ8X-Gk!Y#mwgL8p /!݃\,b0 p_!u'zB"0pW@gm֚VzɴĔ3;kTwnzR/ʑӛMWDdm7b' @Ft=u(;Ebc/s3[@ x5\ MrMdz:0@n+'VvWZ@%@saun ._ֶ^Qk(^ A"E'^slݜ|'[C-|9g1^Z`7yt|Mx8gFyD&1 O/uq/_Gyg9Onۧ䙕-XF\;'GZ@pQDF]l $du:m  Gp-p&022:a\9WoZ\Lc%dpw YR &\ގ;1r#3(9[xD ^߽`;wYS0 [Z%*g˧~oGDdSL6^_Fǘ,NMP`J^ȼJ/xu{@:Oծӯ, Rw>WbB^xvլ̟U-! dXoDdU fuh୏ۢԇYhY{wo_N" ~Nn͒WZ G,izZ Ng.IvZ;ہhJ9m+'ʺb ;L6|HN'IiJskP:;D m6J5(Zh ?-n_TUw0=w\bXS{N2-70*ߺw|KɎC'-7_]͑ +@@"w)JKTX$ـX6O]GCg ##7eq.@wv9'Sم& 0@n:RhiS"^^h4Cԡ}*}Rn}mW  TW 7󡖸'~ w?W8_wԎ@RwpDwm$+y|Jw[=>N.v[G喿{J{~zc^<*kTȎtNu#(PVV"=JbSn(8ܴ wo|8it%G7/?nh '!.^hq2AȌܝ;9?3@@s[^h> Dȴ@RIq$0L#7NBB62_h̋NNkZV^du; p7 ytU3ZX{Tf*K"Rk\/X= Wn_%f/.JaZ~SPRY)hh퓯MȪm֯IF uH$";Yc)C2ЬO@(!'Y쉎Ϭ<{7GgIM @|cOL8#PE3)<.ۣX\QlnbkiNqBs:1?QerUƗYyXz3%BWWAu'z \^d5<(RF|\6nx3!/d؆@6m_=AB.ZC 3%*z^w7#oY6 re}9)ztk !u%3}v詪*5|Žg$wFZ󕟬2 fWBy{f-[UVogEF #coe]͑S" #X@%#;H#M4@@ C:@ŞH;+B3zdnIq\;'*:{?@@ . ؽ3ޢ=A07~{Ԉd\ qY6w8qF~ʪ'=KjB&PVL>sgN<(Pdt{Lqj:Oe rR*Kxtw[Q-_}V(c)HKOB$);g6˒7f=&e݆7ԼtzTcʎL-<.4`‘n/=+ǷI{E E庹9皂 @5Z_07ƥŒ2d@ dW۷n/huu xH MF,f-ý @ (%:ő@O?:$.\Cw=~\e1hTdѻXwx;+sfp+!d3UJΊ∔ 042&_M1[6**E phK$<;n(;kSufv7a|z:vqVпUvˑD-Z@@IIԶ d"$cp 0  (U,Y҇^>("ey4cIx`E V8@ @ X,m#!T`]glt&`uU3$ Jv v5*b;IM)e䢤OolMȾqTzz 3)0{}݉y~LvȚ*:QT` \J|dہVgC@^ߴE{B~=3Pu3Uvخh¡ Mb>@e# t@|"ɦ=MY͟ iԿF"5 (.TW)zX8B/02 =wp/ܧL*KTzpB&`,5t}eCiV[P ɤ  1ޞgyQY"g|Vh0[ʽO2lV/tzTKʎ Lۻ|k$ ҘzA3Ž)Gg]qĮٯ#WΊ~cr" #^jr/)s WP@[wlL=wPgmxZT_Y"ok! '  &9gZW헉WWޡTdg?=W:G'\XZ;d6yeS/ \>o<ߑ?239_QߩOed-}q9{dUE*K#!ui 7k{!=09dwuƃZTAN繽g驜@k 1x?ep(*L3&3JiXy9E9ǻD^>oJ؊NQo,ã@s\z"3r;C*vLj Q>]be.\l L_}h<2*E*XlFT'o\:ѱ8{ɥs*g̩%R]*Kd2]> Ǥw`DzGO}=ai:/~_ǥ/:J};Lm (++>;=G'"Y?u#^jf @nSiD5inMVmk'dvuQJSFwGNm ݩ_{7N8/ݽCr>n?^@g<`BAEg,P6 AZK8$ nOH9|Rמ1ySWB=㲰:*,J7Ju^3Keq?z2yN[kg# wJVJg""M'͉U?_:AN4zqگ22!V. zQ1:gy]~)ZЮW  X555Yo<4t/Q+6ma{' ekr%jC@!WQOIqbxԶwiozĬonQpiuީ*ݨv ȚMFَe"y߰\%] |Ll@QSnT6m {Nf;Yr*˶{9Q)p Mޮ)͎'ϫ5QjVTb,Sd@ Jd|蒌I @?mbhHSOWܑV?[S)k[-A ZDm A(uJ?@=X:,-Z)2v5C g4PTQlC*(&(+ɗ?f#OnJȶ3%=:p07Ǹ,,pc[3b0"T&n ?S3J&;]g:*(GW_ަ3c(  @sStX<+5Q  d&(-v6r(rqgrdpwCl~xwz|:X9 )  L"9td3ݵ~@ j]47Ԩ;7ro凤{[ IoD3VHm QJ Ywt5Ԏ%;${3K1]+,v~ӫZ-*h-zV RH߿R^'+fXn/![r9.CO$: oulmsMxM=w6ʨZcz;ȊB/w(/:'믒LQ_zqW_S"2/MOR% :^gcgaGVF@Ȩ۾ `F;Y2L@<-TOe^X\Voj/ݝR "SB4ZwzWbFiAmֽ'|wޯwsz߻빰F]@)C\ 5a+'S|o|g&dK]L3Z<.59:g~g/ Ϯ?7hEΪ0_q'@@ Djn+%"9'X?U# ݳSC@ DtA pwԯA-*P ;qf';S? lFgQt]7=h=>^g5*;evlGsJriwhNژkh;$UGhc7^xVmN/W]a]0#JkC!v  ]T:zF> e?C $c@&UսEǃ2c6կm;չKUO*C$PY^>':*JpDVn Ԙ6O#k[zDD%o2v\_S.K e%o;O׵E%tY܍>|j8 w/iӣRkz;@@^_w?8ȣ #A nϋ@ PŅ,Wyaݝm2Im @L"=nT87 C>^${L 7Ș@t6eݯDHT4=ޡݏƿehBOdiȥLu1 _J1UWDmz2;@@@~^8]<0Uѱ%]@"@{Vi"#^Kqsd`h4KqOA-)P ;a;sQg(x@R;smR7WW;w2m]jS{@yi|Zُ])y/ :&辫!. H#`1Hn,$_=dغ3EUnV@@<,WX,1{I<<v-!/կӿ @j|g#n5{|~~wlu] LQ^9dpwS@p L.#{*3^ԓ霋@Gd7]3S"FQ&].n ΓZa؅@*>yo/r5.TqiKo6uUFI+iهZjʼrQAvmܺ|}t' /qooZYUp~hLE/_Qwf;زU~fOJq!D@@%%ryC/` p,`*9@<1ל[jF? D"A=1ꊒv@B_  @ ;( ;wp }Unn7rG(7nmh7g`hNzӭH^~*}QϝjXH> +ΏQyP-tlOcV3l@s8wiX';@@@Iit۔l ;z;A @_viH@46XUJi@uPe]sLN+Wf( ݾصF@ 9QD{*TN\'^>H=D]`mᄨ˶JYaDJ՗Qo]lG 7 _|ᏯYSKShׁ*t[TC-qiN 2z,Ccvd8X)߼gis+Q@@@sWHVa{}2#-D@ @T|} z[e-'c!8^Qn/|@>go*"7{r|L%ӋH,{hL6d;΄MH`BU>7}_Yw {2sr FEJ_6˨F>lG@@$Hb? DvGfio5.: 1܌DC r/IyH] tF}Wo}paǙ鋱 軧IM @* pOuw9)LNGCtm!KJEYtۢt]LlFydB y?* /xqdpwwV'!a< 'TtU aaϯ:gmAy/p5cDJ 읦IWxX<^\%9N3slCO\=uS^V7o[GgSqOul_S9WDrYe?OEŸ=:AOV~z|KAAE1u/`;.}@@ }#_= @.O@/ 4NoyZw5/:+pwhg(V,? Y2j8{4<C*:v]k/w!͋fgbzrk̬0pיu_X/}\r<ld_߱ݲ@lS[N/GF?0zXm]M|G?U޵֥b,u|wÊف  r G=u-x4De  @ TO=KDj4NE| ]PjCB&@ww&|ǁw*|*+}sw;ųmOF`B>Mtvʾ*gA@%25ǿuow|B>w?"_r sŏz.xz0w4ɝOm7ӣ.@A@@ L%?V/S.ˍ?N@Cdpd@*Wқ[&v/+AZ!U_'v@|-0J_ Ub.镞8rCVl?~8@P2|miKA,[GǯQA;dp"8 h]]=Ww2تLL:ޣ2x?i[PHU@?\1eT׌TfT@@@C^+qo } WpL@wŽCWxS!c"=Ms%U  Ce  (OzG-Aj[N?{r؊@{y_GΒdNp^Ucm"Pu_x @H>gF|px C#crKtwsǀ/]}5JCo[cgZ@\d0E.M ؁  XY7xO @&pτ2m GޑG"u{WInusUI1m@.P3,#UrUiVȲ۳<ElYHȴIuP|eiDz߿BJ ?lȕ>`y8 ~*HTSgϩZ"Y/^eԶwŕAQI 6v""RD(  Q`RY4 н7DQu! =s@@#"ܳ ^/}@O 4Q{';N=ii ٨;\2/Q@HE`s*g[nr82 IE隙4vn18tw۝@P 葭zӱϟ  @hdlg9uKx4 R% 1 &<زX<.o|<N,Q S @Jy*uE9'?s~!xmQSg4x#஥oYTsi'M.6~ӻ3@qE3#(  W "jS 'py騘:@ Yd  6XRU9J\GFTJ8+duL%em<30 dT♓2^{kW4p{ q;$w=;{ |,q&k _fY;z/@o /MNM*NA@@ eJs({b=xCtxj: xMi˽I<~?[d㮆0=-c:VHKOQi&$>qwM[SΆA஧kklϠnVAǹ @8{] Q3cx;@@@QaaM@pm$Ƚ8ekz[ݝ2>pד@ ̨"sDSՇ@ٕ6A}/𾫫%Ag,R >My5#2<''  @$mG`O&⑜_gAB(@g>#@QVNf\ |^y - 0! L(PV/}B(MmxSD\{ "\gw`$!#cO; r@~6)sjN@@IGDՆ7~ OL@/ 𩎗g!gV^uUt(5[B,#Q#y2@[0ՅZ\_-WUؠ'/ܵo]?yG@M[':*| jN@@ 4tB4Zo5"CzxCwo@_ 89]%TZU:WVmfd:l33>K4d1Z-wm}dqwDI @(h릋Xs+fH.WAC`   Z>cX*#GtDHK4\v|>t2'PT/0;2"- l{Rn1v ,0Y>wT 0es+'8N~nY)M`Fybu;T rE3e~MHH<$2Qco oo"C&ǞW̌JqJ@@pS`D ).٣+r^g" =l@{׀|:#w=H'YuprOLΧKK@p> ONvʧtǁ,"{dZ&  @Ѩl;B P[j"o@By 6}64 ܷtw`WMww7rwwA ø~ o_zG5#ࢀCZ*Apsu6>04&}`q Gӷ--xjTJ n7Eb'  @JJ#;x/t":^h\q^lz`RFrSz.E"&U!՗T^P3U"ӯ헍O_15.\1]*Yĭ/ylR! wsUuʹS&GdfHD@Vhxe;ݝ^@(@x[q)Y=2w.#X#W7|'!al^e 6G_;$(mT]rs{\J/zTN4"ٶ!GTRawl.j{Ijٞ#N@ ֡3dݩeKHD@@IIh0R(a3豑ё4"F, D"懲~[pClۈ R'IQ 9RRkP)tS$A M9ϔ6d9qA na#>w #2M.O+f; OFϿutl:;M؉  Z|yxhסּ'Ŗws+Bo A '!jWޥzz=p- &'}sP|r{sو /QԿ-M{[L;^7\9|iD@@ E%? (2H봞[xEWzH?@ 4B]®`B%,ow(U'y ‹*]*zT=IR%ѱ|rKݢ/K[ϱ(ra̜¼?{%u'zc@ ȟ-[M{ۯ  7L#L&$-r"\wO Gl HlN |v)-U'2;Im "ͪh4 Im?VYw7#Q;lG@iq,]Agq߹o4e4t}Q G޼}+.Ʀ^S |@.HIn I !`z 4n{ۻڕf*SYc # G `3\XbPA lNg @zkp ii'KNr{H#6"  Й@rz44:8 2Op@O9dF 'c3F" }馂{, @ XQ 2{8G@OK=_(9gbÌm'ΖAEaT7W:1x'k 4y}UmV^\z4Cb#"ӣ}IWۋ<Ă  f4YN@}/uO {Hpw\3RΙ3g[c|7(0卵R`kw6#=zN{, @ X@O; _^$ W˱8ZZ}2++=7- :й'D$RՠePHp66kclN ZU[&Ζ.oIJ#U$숇u  ɖöA4y|=5q:A-E^ͯIu&ЬDyo6^ *6FO@ b/PU$b9eXݣir.5_ؾTɒ)I?]>l@祝K,Pv|Rۡ@@[vф1åcp nuƌ1$pL@|e1 FiqcRBɣ ;FI`ʛkᗖjn#Xۦ# h ]z'. Io=q2)PM؆ `B`զ ޔ-BQ9GHn$b#  RSej-i'&i}1N0 +BԲq$ us >*PQDдr":}KTZT{}cKT@JI^U5k\K<))HKᵹ[Q~sՉp8~[ٷW |,|)֓ǩDq7mCp&˂ @(,P;-&'*Itmr?%kvG{B@u䦉sHnŌ?H7t\Y=:I/~T{c5ws'=@3FuM͔;ܮ4,Y  @$YrFU`Ι П;h @'j_19Vhi37n­^-\Hʐ@ O>sZ40WJzd(toFY~|yr>3>_ޝAΧ--%Ru/٥NzpgK]cK8, 'uY!2gi2#5X@@@ zYJ|=.?I^r/t $JA$@#c"XA/υ9 Ё X`ɣ,!t$jk\{2:Fyrd*L-F‚6>^ >vlT3iRgam)lg\M^>~ KE@@L u~"ء-YO7v#:@~P[V $@wfl>@Ew@:psENH,a;D@}Ogr3 eUXZUJ?MnMDn{yމ$%kz"rT9g6SH*"jQ==_=PRlC@@ 2TYhVL Bz@ j_R=Z/-ePS@ P=@O9C"ڗ'0r_ޓlJ b~^+ >kJTpˬd9}L_sTӻEC,M B[BYn_`G@i5M?/O*s)Hn_kvD@@TIV(ya9 2ӌ"kɔz⩄v(0qS    1X)1Ӏ&O,Y2%ΟlE,!6#'Na#4(?ty%q~-c;0ug\!?Ez[en@ Chd[KF"[Gd<*'Qu5ij $f=QUF_efי@ILwdOEJs34S알O`'z@@; def-v <@ nI pi;FkQ6wƽ3@ TI ;&e!,-':g\w ۳7HeucNtpvUﮒmzݿ_/jJzjRۘ8I%kN*T=I}:>GB=mh{RĈhX_@ןޓ{],FF7W}=j"  $^Pi ctKD0 CHpw2,YQ Z/2F4sHNqs#F[ /̕i@а2߿-w<>_>[SZTainsoZow|$ONJቁN'c}] #I xO筽 Jpg 3U{EOJr?P@]Sk p_'s v@@+*on%hLR' `H`%CMi UvyWa=(slNm 60E @EGx5D>Y/½ͪuɌnyY)2aL_7SrRv㪭ou[e}p^ٴ*QGQh\kzGOWz{ inIIЧ=5ilȶO&TZ%;.F@R>e U/2T]"'+AZj" @@5M4iQBOX(BA- m@Nv~p^фV#;mĆ Lzy}@Ua.tERU/Nq@P"a zٲql@m>okMAr2zh/TQdB4dö YL+7j+ٙO{SN{{O`:]BUq|M%k+|npL%4@n^c(LMI^Ci  QHIN_P=ʬ]. o{];"{# @ `plفp9eL:{)Ξ_FDC<+G?8gK)=YZ@O>Fr2夣{ʐ<P+%}I^tI\q:=}׾ٶJKV$Ϋ( 9rlq..8[*kƫ  <(l4@@@ZJ46SJSR<Ă8]w0C 4?x ,r4GsĻ)ϰ@T5Le-i+eM%! T׷w 'UFϓڒ޻IQI$76JeuoKf\Z) 5kVϑ>O8I99ޥg~S~CGU QHr+Ȫ~Vtuһ S~qű5a= ` uO/^_i(dau0+ @@ x^Y:Fmdf̈l_BDH@{j 'o]](0gYn;0Z+ܭ?GD$Z<+ѱq`#o  TT7{n>#+E3UbfKyRIjW?LVS<ڢO[Z}WCYU$ RnT2YQ'Uu-} ܌#=D,+w܋^t ^9w&U#+J}R✱Dzd+c4 m{f e=K<@@+>Sz{bg5M{T6.=ڞG @LHp)/# /K`N{< YUܽTpOs"v"f`~LIf:om94S;8!HU__h: XԹ>2+wzZ׏;y:I(M;+$X>x;kO}& M!^ľSp`OO0J0G@@2*ZoL<& @7j|-c֤Uќp/,!k7|zcIN #8#=Y1 @ {g՗P=5`=@uCz_iQU}@!,0#duw~,b ؾF.ݻGJnOO]Lr;@@$-{PJs>4k}喊`@If `M-=lSoqf @D>"rKN'%: 6''JbѸ T:\CoV?U`uNLdJrunG/=CbdlBb+pj9WU W&cֱ   u$:e$$D@ Hpw$3D@NE4J[s*ꥏ7_=v/:} u: f]{z_c3`F]EO/,@{L x/k[&C{]͒?a@@[ǫ^҆QgA@@d˖= HtOK> ࣫sD@M`ۚe:~ .2CE<bO%r#@ \y`;~Ip&#=КY2nxC HEwWq=>Db6<4Dp;j?MNgj%^Ipg;  N ##M>+N1=7JoJ{C@?7 @"(B߂He4oߜ!6[W*[h2GNᰒu[ΒC"  HI˒fIٴkv;p@W ig `E V_>f?L]̙o(=*:@#T5d6i*ODQ8~Hw%%Hp܃M\ O갏%F{$gj2G½޷-ٛŽ c7MEk4U S@@K hrpK7Z26w$r$` `-2ɚA([ew^BE/*Gϒ@ |H(s,uҘb%>@}W$I{34fMCPI(75{g=.?4Ai6^X%FO0NS_k'B@p@VvU5f6hMZ2N/Uςbޖ1K4~|MJ:@@-zG5yr84ƄQw;1#Z jdvF:dC@@ W^>QhDw?>NEkLS=X%x~o!̚He4S M.[RKy*G6_X(5Yj1eLl/)΂  ɑ=E<7,FWw#CvzS54b!U8#tzr  ZλIi۠|;#4dƊ~z09}lIq⑨$)K[*v[RDFy +{[7~,Zc- Ǜ^7 wJC@@ƽ@Xs_= %Hp4 pZwX i˅Im, %Y+._{PٱS\&pr$2킞US:Vt.'Gvt_O=[QEIIЖfnso|MrgO jS\rޕ 9U>W}YtA5FC@@$Wo__Jv͙&P  ]7@ &ɍOkb9F,ěk"j;f!@TD3{:.]$@`hQz)j${ q%`<ツU:_S;[ڳlT[|l4M{EnYV+r3EUvgA l]-73S.yYpI?Ej{?d}01 @@@rQr"R!MX0.BB\/@ U64J hz;,dЃB!1% Pp\?82;g߮?]2b_eS>? *U.rͫcA+P-t&O].7ecielG W6ȭ,3~9g(r3Tb{WxXLJc@@@NvAvMfJ' q@MRQbs[-Qdx"&|db/@ Hڳ G'ۉ p%[z\b!=[JR9 ]oN%ҷT.5%Ix)RX1($?;EuX概{51H6N~h`*ulW2%~hsëguO@M]L\N%mʧV q+Gc񆻧!  X\`~W|PxYb4 Hpw|2p9>,tl=|Io-*,  i/f]7.=Tu]@jGT5Wr% s*Zq$3-P8 au~(1+VQ(Wy'ܲ @l&"9'/4sw(OxP4;;  [@޾fnxhG@ x>Tq7n* Al-ূ/̖n/sR#@ F)^d'C{r2X5IrI4mצAs6 H;p雫ɘd;!duS9 .PV9*ַI-M%Uv;   S`灀U>Q݂BH |v@ 佪Bn\֋muvTp8 W+앣'H~vA8J 3-Iq2bpτ Tp613mڮݾg&HdtsAw@$ΖmYVF3vT^~kgfw鞥~^#X@@@YYrk{;]*^nxD Z6-)[ŝʋn{2^@ 2F攽zLr")s8+,TWV6{B8 QHÏǏ-jXܝ>WyD7,^W.4C@M}ho(kHξ+#̝- *x$>  .حrȂi^}N  FʳCl a) "k7@$Cy3@(;9}̍3j~%}J%7,z@t߿/KazzU-K;@L<Я`SrUdcU~/̟V_<7}puO-vp{3.|CdTMF<)%; 2  ʐnI EH G~@ lhJhUss\f*Tpw󳖱#n-mKra26,)9AY">=!@\{3GOUqwÒ>v4RUpC*Ywɺ}n`bhi[s7˷\weГُcK7eY?*kwQz$*Fԏ7^G^]!n"c i,)?ۇrO^I/.J'}td֮vKs5^   й@Y;IIs3&WB Hpw3xB>;ߜMVmaRyVE @J0z8/'] 0ec$0.0WgIٖ #`k~pz'rW!U1Z^Rl-rSW(M]. -'8P$SN+8]>t >DMƩ߹a}sɒQ*oGm.   `@ 33],z7 ~P %~%6vF+P:{{*ew$0y}{@*vz3L$5zU?_}\~K'C}DĒlɚ N'9iwWoy;ej%^#4Ë^Ut[Uv+ `" e)q?~^~=in)soI3$Y@  XK@CP8ZsG>5gEFD Jm u ;U~K ѝi,]Kn*T^tsQ#f ֬;۟6DM'yO;5 tL;~v ##Ip6dM*|7W]mJU\Ov^Ill)KYچf@*/*FkgR[1FMzq<ˆ*(nIp"a @$(wªE%yr !۳ _KFʥg e5tG?c[}Ɠf͑W*fײ@[E#H^vsvњ=s\݇X@CS|vԾIf,!]At$ b1>D@@g 4' Yrnwjر }ggȅi6N]S@jEnU{V}~@Uv7,bۦ|"?<GSf~ /. Vh,Z{D?@@@ T' SJv:%G  =:$L`+pP`=h9'z%퉞C0de*g4@F )^X" ס9o13=z^UA;tj܃0CQ#T{:1@Ot_g} -m.QOze82(_I5{EOhhl,C:E]Rnq\@@ deee{0*ImO;@ n9!"@k&Fezgv/,ԫ[Z:)1z:G#*c2鑟)t-˥9:U:=.@(>ǟ(CwVx<]`zZj5+%Elf) ȀN&1mjRunMT/_S#Xv~l+ҾC,-0=Eg-=-9j   `J`W\(ѦCcMIIb@K n @ 2uhRo~Y++;%:D:zdăgG@Tp7LEC?=y`[5_" ו؋&S1}'ٙ@'Q1L@U1NkpsKL D%g땜Y dj2&M7ߓȚ-n\ {dy'%)Icz-\\UiV]?!KFKo@@H@vv_2Y,' >Z.0B0%@).#>O&'Kn8_&/S,VK!p]=1|,ۍVJ}cMqL_\6FNWȊޜ(SJ?!xX'z¬eOU->m/5(פ8+uSϝe[n95J'-tJo-e{l},^G>[W*~>D/ڻng #  \-e~[sz} @nF ͽw7?p 1,5~uֲ!@k iY${ic ٷWɛ 0@\.>X~zh閝`V;q7cu¼Zͬ@rژBy]"F޴)ejmCUݫm7Y^햒F3+a$Kuw#jSvAdkgSN_#?K*=/C>Š20!  @rreކ.C1xtΤ 1N@*@{\9 [sOFocKm{[**^ }QQ'Pt *]5 l~tSSm|)]蓟.\1VݧKag;%;K.;ww=* _DsFR7 Ň׫R~ޕ~sOf,vcTg%;wt/9תX@IDAT2vhOLhΣXZmKj+o]m Smm'S'Uv }fj4F@@w쎁p4{l6!#t @{(B*}EnzQCq/^W.oOd!&{5i"sRsB0 8j3x .lkRUxeA=Hhd\W/$OHₓK^vi2߾ꀔl5fqJ>o*?]L'wשjUݏS )ǫ*=},?[[yW|퀬S52Uِ48؞   vQ!T78sqp NUƄC}r:Z/)| `1*ZlB^ʉɋﯖflReXs1 -\9N3ܴ ,>NFš$\z`yզzkjTU*,+ni&Uѽ6 Dh,Clv;kں쥲WǩdePQ7镟IһApݴtoJdߖޖоulY%z{*{WiWbA@@$VloSUk-8\:vfx @JL^Y<ᦷWߌ191M;e rÍV^~vUr@N|'trhVX ;3EαrIVɜ{\Э&p\rPIѳ;]' T(Kl.?oeaGdڪ~@3@⸤dvsT9  D] +;GJzt&@tN/;5;{]Uq?qܑ Ԥ/@TpweTʟ;Mm)oKt_E]B'N |g 7ݡ# ?,;6Sc(իt]Vn47B%X]xJԻօ7ZMz?cR2Ǒ=g K~#S KvF-]^'jewuD:KOdUv  8A} Cqz{tϴwBl#@m@@;gIф0-!P^(X'18\cxT_ D@$ `] Y~o[,aE`¨HInnrPzCN%j+PU܏L^׹^T&Жd-c'`3Gk+X.stoMmAJJHANJvo%3GTխ:)iՄVuVmh:uW5|-}ϗz򺾯U=='`uv:o@@p@zf=@vNROK@" =B8vC.΀&gX=N7w3KSKς @,/@5K,Y[zkQ3+\:JF )zpso\ӎK& _(ͭFwS`1nQ/zEoSm HuHu'9VȢ$UO/=u_>VEdz=]%758ȠLqJՓ&gu    t+mu08hp .h1&ܴD|Fo7wW-ub%=S@ Hk)r]ʬ:nD_;,\Q*OD{buO CdrlP=6 ==!L O/fm4u@[uVCc}FռdD/O!Ry*= 5_&7!&_bakrU2Jh+Lnױ   nHIϖ5n][ޒ]%N@$% `7ĸSW}pjC_߼sp@T42H=1r"|nx=_0AwI]0T?e$S#Z;R z,!x*$rfwzHz&rЪ۪U]O|W-zG3S4HP?3tC1  M 99I>YD-;?~:G`  1;"(7h/TC"lUقS^[!w8!'=,"[̓m2mzyQ",;K8o>0jTp DsJIlپTe5CT SInУ%[eBбk ;    }Zo ]BG?#G1K&a"Eg[U^N$)ϔNd @pa[l6oR[Y#"8q|?_+|昩e  Wdc jzr mI0"   2e:i$XM szG@ x<6 p@jq˒sZqjZD`ryC%IƝ@@ zsD}܈>rGɐIRZV'>?_Z|bpYk/9F~~X夑kD ~:=kI0کJwv1   . ˎ2nѩd5[5X4>BG n@;M7*λc|˩q@@Cc,_W,%3ef@F ȓ/ӎ-V 7d 8FYyol55TM-rSh4F@@@eZg˳XN@%;޹c[.0B Q'C@UfrSaFzɬޝyu{M-PZ 2EAGAQAuԂ谴P6*mtI閦Ilw=g[ũ%M}}?{γ~:s|StuA @ LNĶ'Ʀ1nt-@KS%⚗W^2Ծ"xϺ_q~%ۼ-N @ PxɡT[2GI?rp&@ {@qTET#:>Ve ~KoYS) PC&Cq㱾:%ڿ3_<.c{T*I3KW ϻ`y8=IUu ,t5^ֿ#Cb4F,=w2iL @V!844Y5j]]uOKSԓֳjZ~/ySIj!@^`v#ާf7ޏƮñp98Z6M sU]q]qYrEg4Ϲ 0̖ dVby3@ @P'rs6@; jHA`~uD"-[_/첕^ @@gѱ8xd$>ރǞ{`$f5Yg/kgw+ܳҍgvJ⭧.`D`OEo}w8=K_| @ @R 47Ǧ'&ctb9.~._/E#@)E靵}(y˖W^7הl @~ @ @tuuO"9ݘT~e&@}~B lvdz|MwDZщTD @ 07=} @ @ $Hxʯ7$#@}Oi̝ZʈR֮h @ p*˻[^y4wmb @(@gww7RWLP`bKL)`|jغn޿S{P)jU$ @[EsNqN @ @J%=ϋD/ܲnG3F /`ؓ,49]əoi: @ @ E+KVdrp3@ @P;L0yi"Wg*fJhx\OZl{{kS܏! @ Pxp\~2=Wo6&S m.@oNc>ZOn֯p @ p"?Q?oɏ/Q @ P|C# 1=Wr&o8h 6/2 {~&YGo ( @Vx @ @@a:_۸v{IjU&  &}Ok: 7qB @ @ ص/s @ _.Pd<_77#/➾s{Wj|r&n9N( @X:qEޙt @ @y'GOT %ػ{` @g L &@߸o#w?Ŷ'.@ @ @`*I/cj&t @ @9T*q9L&3F&'~?'xZw U\Oz8'Qu\NVC  @ȓFGkSHGmpό @ȡ@K{W?:d"}MVK>v?~k?|MIN3g_r## @ȋ@WGsw sZ N @ #PrJ~~2# @ @< ů{O~ @E`矙9SCM @ @` :{=ǖ0OA`Wk[ON& PbKJ'@@&j_ mq?)]} )H @hllܞ_$]ėM=| 6/  Pnx .a;}wGͭJJ&@ @ \9gvq70{K>A @ @ gIsGY*q%؞XaɳPH_/`כxC1VWᓇ߳/6|7Qd @ @hlď_yf"N @ mqCGyVeE'@ ^5W1,л$s@Ex'Ԣe @ PEGkSGmpό @Xd# 155"g g|!k'  @6sUM9 $ML;y\`?? @I9]yt>  @X$m }Tq]4ЕJ$`{[?r(XX8|fkX L @ (I4TelpH9 @XJ/\&@}sAO( e[q @`&[O?Cb&&gb_ݻ@ @S`95ft@ @,@KGw}O{J-IOo' PR/wNu]|Hzٵ5q_'+.?3_3 @T`WgN? 짿gH @ @ZZGGbjƾS[F?m\{K5y  @Np/IM%X5q @ @EGژ6gFӁ @̓@SScl~lxF3 LU/&@:XyX ZR19NOཟ+ƧNo  @ @@tě^ՌLb`&p: @ @yihc>"}Ovm :  @lpUGE8e#s @;zQIr{V1  @ @ tŝ@FXhtz @@} ^:,@Cd&hs}Y/ @ Pg~:2Rt @ @NC0wU; EZK{2I @n2 @ 2н3I^ԉMI`&}?4<d1 @Woޞُӗwd?=D: @ @w>0*$OML; Œp{G8K`Z4pR/o]UL @#/='^rt7& @@ ^kB,L-o6,7ԜH @Ioxir=3 @ @ @ckW?:K h,kJCpQDO?~{`)3jA @J o$?3s{1E @,@[[Kܾmpa7 ԒWcu: @ \ wO.[}oA&0 $;[mL @ @" T*I4;{2şܐDWkzO; @ 0G'[k߰}= @Np/ګ &p?=`x^&fy  @ @?em;ZS3@ @N&:Y3@ =$@ @׿$Z3O3@ @N 'L쯣?X @@=^XJ/=̕gnsD @ @-~5/<ױZ =3 @ @D3 $MN Fb ^صGҐ}PnK @^ghlySK @.o_zsO.&#@R dT<%@5μ?|ewNbt @ s7|ۥSΞ>"@ @ Mȸg[IRy֭7M-f$@4Pu @ {{{.OE`xt*ښ"Si  @[K_G"vsuQ @XB mo >' "@XyltGp CϓGy0 @ @Ͽ`Y暋2?  @ @ںb5|wL|7^Xw) @@g{s|yS6]-=ŽDO[ @ @:cx""dG *!@@ni*}B.qO#u[_N @ex_̰wUgvӁ @S Ix*}r1V/# PO6j۸KQcb6{>)F" '@ @@fOb"LAgӁ @R;v걨Z4JZ76qqD u.P44ξJ?u]݃]N @ @ w :-Gw3wӁ @ԕ@[kKܶmjbF @@NrK,(`_}vZ\2o`\ cyO{ @EB=3uF6%ْd1 @'ɶ;4VO%s-7|TW ] Pj4];P \L)o񦘙dE @J(_l\y){@ @C+f-cO% PNp5U '0ܻi#)\}`$.==^xY%^ @ Pv榆ڟbz&#=)4&@ @ 456ƽS1:8vAGv9U2 @@} 8>UU(ʑO+\KZ  @ @?ˣ13CggFӁ @ /0ǡQ$}>E*N`߾ծKLj[JW| ƑW\\" @ @hkiCqϣOeh2=56grӘ @+>8Pʗ k]ULyp{VDXu_L/2nۺP%@ @%7]ݳ^➕L{ @(@CCCܳs'  @N<( W믿(y$@hTGE|Ns)Z2JT[2f @(K.=3t5Dr:V$hL @$>0(,hyλNJYRg'ꪍFui/]xhխ` @ 5}/ q,=S3@ @T*PTTk`VH"y_?\r @@ ^kBN`%.xIϾ'cs. @ P2+)sջ?3 @ P֮ X=7( PԨD(g}+ tNDgk%^~] @ Pn866w?f3-I7'T= @ԕ@gg{Pzz{]UTj?<ww#@ 8h+&/J"mu$[ewd0. W @^g~l)G|__ @#$I}31=nQuWN(˳*%@@{޽5i.| (~(~;'F L @ Oǖm3L;= @ _eqC (ٳj dў-gXr'/@'ERTq$>Q"Q @ @_]͙'}ةvt @ @ ttņ.@-$=3 @`tf&@S|$=}~ 5Ɂ׬9/Zޙ4" @ @hmnSG, @ $d~"Ir2od =OB KnN&0YPagNZ/L-Ǜb|bj" @,;)p,I  @ @`:zc0!K I*]Ś< @ ܳiO.0Էy5O$-> $hW9׉ @Q!}OD7'ђd1 @Xj=U to +FZ)  @Np/ʫuM2.u|xdǁR( @'SI,j\#Nqό @,@`%'on"0XR7br @6tM" 4̾+b2f>)'X  @ @M74sc}N @X2ضkx7qvqvm= @+аә]λV_SK_pz蹘G,"@ @K*筈ңO9=H7?\ @ @ -qǣ#15y^-yiMG:ȟ& @ ګI~M<3|xɧrJ @ pM?pE ƦM @X12>$st'r{FUE6_Z7XF@IDATc;'0]ƯΘ^ID @Xmyi͜bj~M @X<MhyHڷ@ @"а(̣஻Y}鐳\wh4Vt5U/\U" @ @hjen?`Ո֦$:[L4&@ @!0ixׇ& Pz'W)PMjJ3}9S[㱝Y  @ @?vemkf: @ @ MLfyHg4/, 4 0ý[z.z25;J`>q(~; cP%@ @̞PIb=OqoiLOqou{~VS @ (jwo>V @@.+o  P`om+p ~_|tu+ @+_8 7@ @榸kh}S*juJUb  @.lpeT)uMSQ+gŭ,n @ @ ͍7]ǿ480E @r 0<Gfo*@#=V @@lp/ޚILu݆=!̹Lr19~R"@ @%xkz+sh @XR%`Z3۶][ @4  @_MQyG:Hi ?2+Z9MD @JMuk_3Ո$ZL4&@ @%w>6TWaj|o k XP ,'? ď P<؟~_;>5:Tn @ @@i~{_ّ^g&Ӂ @Qcqlݹt1:RKu1&2X(Jָ } [IMvZ3=crʛ!3 @B`_oȜu2}ټ @ @t:{⁝C;,jwG6#@}~=FK%ZT޹Tӛwn8Y/ @ P0yeq᪮̩gOqq'n: @ @ZcÃs@%H"+>$̣@4jn/@AomdmZ('@"ya4IOyS&YN.p?{Z @ @ |Uŷ\yn* bԙt @ @ST*Ⱦ阞NK %niP%@'8"@ |3IR}gޫ-/_G{ÎЛ. @ p }`Zj+Z|o/*0 @4N @ C[vfe1sP Mh|5#!q  @ȩ}@$ @ @@:"W? <\ @psx .03޴/t'K=^L @x[_ߦuMۢb @,@ccc;[%Pb\ ;'S  ݷx4}#D&||sڛLhL @r \9Z \ @ @`>&d|>2" $Q8 @ 4,L&"@K 0/["K`zSQ`b{kh>u#@ @\s_|4ҎNEI2ј @<[+n}p9$*o}*!.`{v3= @`=#jM^OU˚*  @ @[b|r&zhB'#[ho= @ oMq161oGAD߆*HZ1  @@fGf&Ӂ&u;$~hOmw@A @Zg~XӚCը2wӁ @<-04G'iO`s{[b pNp?u+-  @CwKO^S2J}Z7hn?[J ` @D`5oSS%ovWjDKcNq/ɯ2  @ 0o=6O %?Ol]D_N77#,@jEWߝ$OӻR|X4VfW?t#@ @3n}"F7<^sz*Q=,! @r"w>6ߜvKVǾkX%@lpnT`xז_ӞP؛=8UOi N @-PIw_ԛgѐi= @(;8Vb–>4 rNNQ ԉDi%;ꤚR -f @ @@嗬\tjLd @P'9Tʋ_r~w-w(RNA  ԏP鮋^P$?V?Uccqd`4^ső8J @L`gw\Dk)9 @(@{{[lv,ffo*#Ol 0'ĦY`xזݫ0"Qv]Ͽ(iveR3 @O`qCbρc81 @"{@U<ޯuz{VNb@et#@l;]D9 wFrj @(omM滗͞p>$@ @mDHz,@݄޷KD rYDlS @@~6ufWz7'$"05]v7yQiC @X#}{4ɈI47z\ @ +7?w]8:]-ŋ.1 sӓ .0GV*䲂R۴pծ` @(/YQf~01qv -Q% @N.[{'ctb䍵ȟ@wL"  @ H҄ùN)q~릻ѝ  @ @@.9'w sGGj18mS|It @ @oC-\Hn[O \d p66 @%Q"PZ-ı @\]ohom\e!3@ @:{w ae(i2Sy{ZXnE @  @@vƿ+=²^z'cxd<^梢"? @:-s'z`r:%D㰾n: @ @]qG\B'?Rj @T ~  @ <~k+MC7+oCtո, @ @ $=#mO᳞ex  @ Pm-qcc19]V}[-`APl?H Pû6uzEZuPNJq͋V,+] &@ @xb˶@[ODUF/ @(@$Xs=<^zTkk|cAMU2ԁt{#j{ꠔR =*e&@ @פճ_KOqyo: @D4wǣ{})[~{ލO"I @ 8eiN9sd%W?Ԓ0H'O 򒨤pw @ @z8sY[>tbΩB;M @@^%W0du=~e,͇>6^: uX) @ @t*$.XaG^#gӘ @9h{B *}7B, PxZV}GZ{t)v?hAӋM @/_ ;P4 @^Rck115>o]{s>  PrK |8ĻOR {ݦx`^E @$o&_Ky:t"@ @|Ԛw3sJi9Deh(K$@#0{֮W"3K#0{[{y*KqiB @̳8:4>KcS]mI5t @ @PqG Y/P}GD@RK~~m>gO @&`Q8%ZCSjQFǧ?>&&sM  @ 0J׽H2>]u$?. @ȵ@WWgz\g$=o'ii @_H @wo>ֳz:/F%fWHr @ @xCd`qF;ٕ۟DSCfј @*w?9n5Wå &3_  @ 6d$@| ڲ_I8_ɤ9U۞]<.:w٩vю @Z#Q28Lh @ @`V]FZbE %j??ڸPԣ@RXt<a.S,;~}?@ @,x'ől3O @dhlGv;;3\:yuQ$Q @@!lp/2 IyۺQLtj#?5ӣ\ @ @~?~c-s%;Vӷ92wӁ @H;?pdF7" 4%wsyŽH!@hRTB_`hזGW_t.fGb|b".A @T!;+[vg1]h$ݖd1 @̿@[[KXLN/\%K{ 8XB'/!  @N&~!d_TS2/x)kW4 @ԟq%+2H5f2wӁ @QRC866=j ܿ~'`^S @.Q }>vV})3~w]N # @ @%HCد{51wk&=paf1Ӗ @-Pk'aGOSwZ @hRTBN`wW_&jRtfx.J_ @ : drl"bEg͍wgEc @8@Www= $m{n_@E%@p{D (@s/`)j~#3VEz @ P|5闸sA* @ @hmᡡʢ%73o @ @wϡ('Nt^|I$o-EuZO j/=N+T @E%'fb˶JhoI)4&@ @C'8n9ؗL6\;Էy,'y @K'8Q`w˓]Y~<nxU+cՙ]A+ @ S.;;>cdl*ScXSLn @ @`.S \'$~S$Y @@Q*E .7ȫd24ۓy'fsxZZ @ @@[~,{7Vw @ @ ttGt/@R?[ ? @@9lp/:QOKj)} =ծ7S3= @ @ Ϗ+V9tn: @ @) ttǭ~\yncy((@CK 0]=I~sx|vƊJ\s @ @ /_y8oh>% @^Nj4Xz޻/-O#\Fǧq\\#@ @joJ߷7S鈖$:[ Nc @G#n~p0I;Nj_l/ U @`Z:{3 @@I~ۧ7]=VpA]֧( @(U_3<4^ݕt|n @ @3]ql,&ѷq  @@^lpEu#p-3~]IRɴ(xe<|0.+wW!: @Y翲=j i陈Nq) @!щعgҗȷᇳ.b2 @%}MLe޽y_5'wz{ŅS @@༳:LՎLD,kO&Lp @ @ h͏(HL)~)* @pl~F23d#jꬬҕ315ocxS} @(~Xݚ9Oբ|n: @ Pnޝu H$e[d˲dRc[qMM23mNf:;34nflɹ33δ,؉d7$HjkZpH,gQLyB/퇆܍: A@nх!-@ ZsOTuV蚒rapT>Q4]\  8H ɼhP66U޷x=蕼6G0  V2ONHn#- o-WA @w,)"8G`sXoZ۫*srg%GFֻ@@@l/pyP]5UKbܐڈG||`ʍ`@@p땳=raC;`YFz$P %  `y~  7Ɩ媮VپO֯QN   Xukk'$5(91-ɝ]F#@@\)0.Urܗ] e Ѿ\ R%E@!qD R6wpt Wp \jD@@ps7>m;c\Ϳ)ށt  \W2{1+0   |m_;*dR@x  .dWo\rU{qxB. GZEӸ=V@@@g iZϘ*4ܞ}n¦F@@G BAu|\&F#گxj@$@V\@'hzb.hKW :5$am*\@@@',[]r:>l yaM>M  yr&Ymw] <ȁ@p@ӞK ^T{ u9Q    ~k*Xa/@@@ @8CԴoQ U  `?vpߚ1 Fz E[jV+eG||RW~F@@@$W*b*-o)8@@@QudQ5e,I@ ̎ Ƶ/wAbot>'|p>{C   :ٴ[.q01nH}#^ #  HDȖl0]8@@lU#g@' R pؐK2@@@7 x=?x4sKKvsZ@@@Pe8:*Yv'<'*ns'B  Yz (&4}C`*ܧȎpF1Gc2#޾Q   kTu&E!Ma `@@S48^q8AbpON(@ v^=rGpH'SqŹr-sq\VO   vhshIIMM0d~#&775   UFUr^hǬ0s=m8  }ք  @aMOejŅ!3.kY,sB嘞9@@@@ZZcOgD<=Z.F@@ !MW x~G@@RO|  po=3*߀Gǧ&C#);K   '+,^;|C@@'u~v53>k \KN΄ X`gP%:{DX]\Lʣ B!  ߾vB2YO):]o@i@@@(T);T:kI*GH r@@L4 i@@-~(xōb9o}a5YsB'K   zP&3y)&!?MF@@K |>9ڠ/'Էz9!6lqD1 BAI) 0*4oWK ~@9)@@@*iXNŬe\  XT@/[4?2+e:@@4ߘ@@a}I 3Vօ_v9uZI    __/ا[yK:B@@ t9p:aI^5c   PodJ@0!04g6qHg i;/zIBAE$-@@@h\ɞ!o7-9!Rğ{g@@M'd<ܩ!@;[`H@ xǍ8osIW϶t %   JۿN¡ /lgZ @@@*UJi;}$C}?ox}1 M܋F N```_6zSv1FeіmUKʖ#   `F \Y!L {$.F@@ T|rTs! =W F@ ^›2" EHtuEi<] {]%5󦺒̈́   D嵲W. L]7./5F0  O@S^ ʙ%̅&_ =0! @ahp/'! PTdwǛƖ[~g+:L`Sg\֭ђD   T y6^wYɽ&.3:@@(eߩNlxَbO  0{J!#  PJB2Wr_V9sx02   XyK7]#@@@ TUL^p8E`R }N):@pSnXejDp@cjrSw# taQFetlBZT4]d@@@V7 ;9!2J׾F@@ ~9ؓ1n]TxWJ<-! ,hp% # PN[Vnd}S9-g]8.˼Wu~e$@@@$m$565CrG}e#  Ex<OTHϠe0xoivE@Y> <.E@CgM۩iWU.z@o/"Dkol01 XFq! H6 Ȉ, !ylBb8@@@@h,#zdϐ9jc&~MF@@ 3M!`/UoG18c" @hp/13  PtDw4V}2&(@&c7  dN&A@@@`^ ?rR'ӦHN;r#@@ #urhZgC?k߬  Y G@K ާ-IH`RJm䌮"@@@J%P;__ozICz@@@`pl94"9%rTE ݅N 뜌4ߡUUϙU>~ypTmn b@@@>6̕c]r:>l*}^&~{M  3 326\hIKf&z%-F@@<O\1g}؞ TD!  p Wp  H:.DVߟrfj۾>YѤMK݋@   {O31nHM#|aʍ`@@xG ˁ3 8'0z>lJ"@xWߕ 8\ y0$tx+׻Vw]   `+7t6 96)R{h"  BÙ;4B@whpW" [dV ׺\הU3&],ѐkP@@@DB~F'">& MF@@=*Xѥxۆut o xp@@gc^{vG7˹KnD@@CZNfإLLx@@p@D!1rp(Vi+K= \EܯC 8Y`$bONӍ%Ʀesԇ$@:A@IDAT@@,.r|M'$is!5%R{h"  BpJڏt7\dגǼ#}۾o@ n΋h@!8iliP rDAq9;(kTpf=   %D mT>>MAM  KBA<;%T%LCjos]V5" j] fH͚߫vqO%$9^*ڪ@@@+ {k8'L52n]=ܦF@@ 9vNKue}h?kO\Y)@@@M^ky}d"@@@n${ʏ̍B;~HN_~O@@pGݑB* gƜQU\MD:T+;@@y&  Uzӗ6R Y7U}}ITۻG@@p@8䗥uyuW q2Sp#  `iM%,oMX:K6"6lH\7v`,@@ @ -8mZ@c3^!/ɣHmu    JCgedtʔȸ!GABABx @@@ ]@Z唩yS)7@@(@UUHvOwe*:yak<2<# hp> uMkԜOr^* 9pITgb hoM;:[w!@[ n"Y@#0ڵw$Լ_}<%[gi ɾJˊE@@@0+Pɭ biSNgD2Yj'w@@GAB* 'H,%0x Śq@@4;s] @$cgfL$ey}ry}YgR@@@h\`bBCy79!Wj=/0@@(@*y+(, mGOxvr!@ 6[0E@jƖU^wZ-7)7䖅!p2   31Q]&!uG  d}F <.YOmzy AqJ  -OU&V){f; #   0+++39}!;@@f't<>>!1F0  1ɵ^#h|.$_D@ E@@޴6ԓv̟H ynyy̍(@@@ ,M?t"іS29{ʒ"e"  "zT6do@@$4I@p@"ֱ7R/qOtb2#;{uKE@   .,x=f*.D!:M  IanR~շ~1 XQw+ 9! `s[jVnWܯhy)djZv'7HHN!   P<[?!c$EVi䞿  /r(! 4Rool=i# <̉@@Ly>8eRH'kE   I~~Y\15{Ĺd.#@@dL;.&'~S7쾒 BA* 8I 3^vfȗT]A'F-W ]LѵR+u @@@ }st)1LTzm3"^ub@@wB䄌MQfx/.@@t Z@@=dƵ_Trd #kcT  X_`Q]X&2yd&E4#@@TFlZFFr&0-7rX] @  8Y`lqmwr&r{D_NkK; @@@ kl+S&7.^Κb#@@+7)W#܋/@@,4I@p@coNq*w_GdxdLW5{hrw3@@@ x6 vT8@@ ^ZofC@ L -&_R*?p_(ˎ^YJ7./%!  ?J¡ i7cJm^$  0~vG-Jc=[7&Y@@84ǕQ@@@c0p^MӞQyL\JM^#+tiIM+ m@@@; {kaS į9@@pe] OxonL  `1-  [ݝ] )nC gbҸhJV@@@2 )   ~w|ɶSɪyĴHM<C@O59h*Hߌ&ڿm}f!@( PeL@@-jMyƃpmrM/dZYT&oE@@@U~پ/n«I$H)8@@l'Ee;[Y&1޶g9#   f<+Ƨ՘5,+U]/w+eAmIJy   VZ''Tﰩ†   TEe!vn͂&_Ƶꯈ01   0;.j@@gE~B;g䈓|M:uΒ   3Jswݍ@VҴ}8IAU  *Qz40+&~MZvWT V`w+! bDl`x}4C{F1\LRWvM   P>W^~d|\sHm]5#@@-4[ %a<v*+! ,hp% # @HSqMϪYĸxԖyz:+/wC̗y4[faH@@p@}uHUF*ǧ7Dea `@@K "4X'D7]S1" mhpR( PMG#k)ྪT;buR_q#  \rwXN {xܐ&~MF@@vK-KѴn|1  `^wf\ %HvwћZiזxj+tF~[>tw̧ɽL   >zKG'Mkr=ݔ  5oOs5VtY}m=W @0'@9/@@Lnj˔ӖX Ҏ.^G{@@p+-+ˏLț 7  >X%^8^ _.=fB@' CQC_g>"-Gd9[i%b[ɽV@@@@ܐՃuou>xr|J$6  `M\sk$kl*ZVщ^p  ݺkCf  D|O:KdV}4:T y}ZYXGC@@@VGD $  %l:0Ls{=qZ-;G@n$@8 Hv75*X# խ_%E4͙@@@xhbyuWLHSxL]F0  %ܾd^2tkLtQmH,@@ ~}" ]Cb3* HJEȨ&ۻd5>Z@@@w>X ?zJrwȅDj#➯q  5o:0Ds{ȭ2SJmxma$D  hp@@XG_tYa9 [r Mm]rgvn/ u&+}['%2A@n,@@@ &ц˪IHZE2E|{,?30$   }stؐ)qC4 hr7G0  @Q"Z|Pk}mI  hpG$< vHtwVS:;"/dmղtMj@@@ wYeQ  Yw7># `Dw)q*.Ү!kr_8,]#@@@I>SN4 ir7G0  54u/, _3jsg8JC@ %`@#GZ*Gj*}W׻eqM@݇/   X}[t]2R iP  F >!aK (@\ @ @@@dHSԾhMAM Tҧ@C r# hpw"S" #u&"Kּy<}o${GT  O\Bu d(eȜJM4#@psc~93)}=Z:Hl?" ݑJQ  p-djEV;W3C{nH>|WsW@@@ ofB@@ %`@@"q"ܣ?=1]I{_\H"UgE   %z4y%BiIM?k$ToTv#@(@UUHK#嘞9%54 ?VZd XOw ! ΃ƖJq Ij%<.o+:*L   Dpe^^l;#\ʈLLԄpϓ0@@HXvOIbLƁF_ۆ   hp  ݪ=mW &'bZ~ F@@(ڰ,嗻cLMp{&wSp# %l{kT'79\/l?mz@@<hp0@@}ؼP}r.Y}Y`@@@ 5V=L72nH_MF@(u|hD"vPqoۆ%_D@ @V@@W*? 3YyA5߼0$k\,A   Xw'xl0cQ*hr7G0  PHT6L( i7͐m7" +  `_G%'^ RE,o!d^CO=2/╕ρ   iǚJn5ׄ=Sn# WEeA}fz0^?Q mN`# x5St Y 4oz9~v}bdyBxrw糀@@@ xkɏm_ɼm:2N/Gf#@(vtqx 1#^?:-vD5 X3 8O`8v0Y߷^U Ȭ#yhbyLNeH\O|mDby NT;52I;:/ƪ@@4!@@@V&KAA9w'm/"uSd}:ܾ﫻gdv6Φv6[٪f&z:$5;IvnK$,lI$(Jԝn˭\bE~=n>@@@@HK*T{nϚ(jOIGdM9$c  0cb:<:\XpEgwW$ =ڬ -0ֵwnק}&%,98'zT^wt @@@eA--GmxGMHR"wG` B@f P^V= ^j.)PSSGj@#-@@ cg@@ }x6.rCx}sKO[`s@@@/ ܶFS =vKgИbKe~ܯ/Y@@ }`(]44bnÁ/}{A@7d@@ }@oXJ>oN]q\)0<28GY4 i@@@ ̍ uYɤ^4 _ 'ɶZwK-|   7h灳:wicLRE hOZw"y.p@@@tJ=KfMN%_::!R6@@UlL'_H7ىcvC dZL N 6\p۶e}$r _UubT޵DEEZ~U$D@@ RݱVot(i4m ݫ˩pwH0@@@"D}: ^x-' " ̡4sT  Ce޵<1kqw ԡȽKTVwWd   0cE!Th1]}^Kӂc0 RTG&}aܕ5."=k  (p5!  b{/U._ g4@IDAT]sEU#ڱ[߽HUBO@@@H஛(66O_Hk1[S^=-8# r9ץ褫& HӖ7@@Y P>+>.F@n-,j v١kkhA`$1@@@x:qIiM<0jܒG{Zp F@WC!m;X<|IұP2yl`   (p1" 3v\am["U&0>Л;:vy@Zz   0OzcGtap -=z_@@@ʃ٠&~-U~w}~@@Sen@@]m}M6C.H$m+GwT@@@@:E>ܰ\;M]g䕧Iix\ݒW PNTRDi )<۲8  0ϥ&s! iĺ#+.wr/JRg5>> /Vzۤ  ^ PZ\l vFSӦr1&ZSNC4! @ x<+=\n$4k1Ksͻg=  %@{Z\ F@V n6j:u73[> ;~Q/须?^;   2ݼZ?74ڎIS_UN{&5@@ "Pg4#\yB0Ā Mw8" @ 흡 M`5_~s;NE} GYP${2   @^ZR@_͟FҊ7|%`@kҒb?'u<~^&ަev&E@T7$b  0Ѯ ǬY!_}:U%lD@@Ȓp\OШ`"ӂc0 @0XI] z^&^mڲu^War@@ P~]N" NnY&g>WO'UZ*%@@@@ 96a61odcqD@M ӄG@@ D+600PŜɭשo~_nG4   9#*끻36iilR Pa  %% zt,i  s@uNxF@9!`۶Lqr"@@@@ ]mWpDnEY0׷W`Vcaq@@@X0K*']-Ȅ.,ݹ#@@ WidܞX/"-UN  @(<  p#S~"ظ3ov#0O&ǧzq֬uY   T*U"N/<.:.y-K(rO   U }t`PD:j,aӺl /r  (r0@@ +75mRElڹ'`[{ZݺP^'$"@@@ ssj?z.Xl[*S@@ ^W*C_Fm-:o"&P@@pI@Str?\ղAQ~fA)yЃw/VI1Ofn@@@ _rb4#g.¥Q[!Ž" ()gHGb[N )"/mEN  (pwن. ䷀jܴ[SEΆCdϰZk*Cs"  hǀ:z$U^Un=-8# 5ދ @6o~%o3 p@@EhI@ C UٸŖk2Ƚ0uN3ZwsWL   eM+9q)ҥ[|ɱ@@`~BQl|z~b|,wL79 bG@$ۏnmrE@jd{q$F"s&0>;:PW+ټL   P>G=BvpqRIS>`:/zd@@*P\^ j:a~I@fi;W?ͣ  (@{. 1! bHh%.aRo==.2۴ygJ  @a P^JV  2XWPi6,}u(pqxBo8WWjJW1 @@@  kuǪZCɤ81S.1@@`FPHMGF\!%{ꚌI@(@ pSI @) _\ݤ)r7RSo$0ilPmЧ;o3FfG@@"bqHwi$˯/+Jy y" i }|`HS wtIS;7ˉh@@`Ϙ @@ne&`EHD `*mz504Mw.Q/Ā  j޴Y P@@GvLxם*`]|,I @p,nE@7ĺ.x[SE!7L38xz@GȽ(+$\   PpX|J?Vnʋ-)rO   pUҒbut3v<G" yy \K :P쁷l 3ZxQҡ\    pY{\L:IJ-Q@@ `0q L\8? piXO'r@@T<8F@n$0:Xjݛu3F9^i)YMk A   WGݳg'ir@@l@@+F:SJ|U8|A ή&'z=_8˷   nXݴBUQ%RkKc7" v׫ +Ge>5ǁuӶ]huq@@ /x[1/@@hhW~j ~\UJFQ}vgKn$ g@@@/Fe}lHh$lp21  -@J~# i u희ukSmiOE=ڧܹP@'a@@@_ ,^jS'cҴ*!@(XR8/l$6g:9 @@rRB@W`t{"Aŗ?+=1{!gMgtߚj-(@@@`+Thr}NkX\x,J)rO  @0Ԯc5~@4a%/1G  P6  0d,; Y8ec Cu>ݶNEA˞  Jඕ5y_=䛡1[Eżt@ GBNd2C#/3@@T<8F@FɎ۶Vؐ0=67s2K! >^ +t @@@:Ĵ}~:~ਭ2S^7ux@(44UC͟֏:103  @ P;C\  @L{Khņs\XՆ[T*L@@@`6ݳD#DZ\2E.`@MLG&u, mHsT&@@ PM'e@@jȽ=ذӲ5ss1<^כM1U_<   .xe00ç/[K%EtrO  @0R LIĄ$Ҳ=e9@@*@[w@@m+Vn8([0}WC\!L͝ HwCQ@  D|Twn\^Js1F" +жNP;S)nӜ`@@"@{VY@]hWۉ ML܍rI>鹤ܹH%E   d@Ա :A+J.Ne}|h1@QH{r8G#$mneh|  a 3 r  @>N]ɲRE31f_dϰo҃wԫ<   @FRZԠc{G6T_.r/={b ̥@ P uifo7 ?U2D@p*@S)! .vV|]۶_2]>P`(6W֚ݴfp   @ x^4E;IS>b6h=/c  dP P˱],U G<";7 $@@ )@@BFZNj{H ͏tRo6w*9={oY( ͬ   @ ȽQ;Etqhq"ɽtr79@@ G<vԴ́@ZM|BZ1@@unI@Xɺ[xzsegJ7 #g; @@@W }z+[Ѹcyi ;E cÝy[ X?*)~?*m&5@@`(p+IA@\"0p=m|Aŗ[.I4HT?\Yŕs4+   @YO=B5<28ܩ44&-X2 c9@@ KPv~>ag)TA۲_Xӎ9!! dNY  P8]MXw*VLR '12ɄTRo4ugM[ _&Y@@Ⱥ@Hlj{t"q6hͧ9@@ s 3(55mgnaV*_FOLR$  0;1  *dGm[+Vl0XfI^':p6Q`y-   9#*']rפ)~N;vc  (++Q׀G;bk],`ֿl:v,bRG@3@@@_ "#ϛ/ks >?8;WVhJW0@@@ *zezwWyN1Strx"ߟď PH;?Յ!w]/eoe9@@@64@@l "n|ٌO`|"7:TZԝkz/ "F@@HK:TG]wvv(>9#U>N30F! cޟР&4f8s?3  3}r\  p)r?hذtP9QzI~@΃t6Z`y+   z%zON9/rOIclmNĎ {%hGD"0aַ#-/8_&N@@=7@@zmU@e^&AY8\p -]S(   @˴NMM'8n S5"wW1@% ĸN\k#p#< [o4  H q@@ -XWŪ^-qsᢴ.f0F`t|Zo4u(uzyS@@@(X om~S E.ӿ(r/XC@` JK3gzoH_>-xoҿ+@@WwQ@@Y \xǓec u,r tgt*-pi#  hXҭz.%(r1EӦlEx% B!>1sRf.j~{lˑO  i#  s$ߔߪX?4G2Jͳ5uz=. ]@@@=VjpOX]YF'wd CpeYThAMNOq 0S[o%G_kNu  \Kk8  " ͟79uD;iѳt{Bw%O   ,Ҳ/;M=:nt0E{Ԍq (-)VoH];?"y㿟ٙ@@QΓ@@ #}+74~,Ȣ,Rp zZ4U˪ .?B@@ժ.Ӷi z,(rOˍ / T\g/۞p 0 ˶mecǸ,@@_߇  s(j;\i%;U^6S3z{gFnm|^EO   ";WתT;E>4F& p׫/41I= 8}}IS{Z^,  }̀  @, esYU2+> LW@@@0iՖkٶRE:;c$ @A :~p # Mq{]@@\+@k@@ ĺ.U7-qZHX9cze)mU|ω@@@}UՇQ[8"wY*J+''@ ^RiB; kht%y{m-/X@@_ PS@@ +C]{GK}gMkؼ:+AhA:mwV\І(/.H@@@6Vk mmV2VR+(r&!@I_b6fr K>+xyKW&c @@A  =ySݯx,XPgcz}iR⪂ʍd@@@fy>h +F^t\ (rY PPP'7q 0k kMTL  @ p@@۟vl5w xngg6 'iS&'tmE@@iyn3V""4^C)z&&?x^o"V?k3[  #欈  pXmGa9˲7kUxȹ@~5ֺ[D@@yUK+uZI q1S5)rwl@@uXKy!`"-H]M|Z"/v @@0@@W4l<`_2 e3zSZR- d><3   W5uܧkF&Ig 9"`4P54:#1F LSS<σ@@ @DR@@ I n;l\͒*r/+%QwP֩ԟ X@@@`^zKSǧM'ya^"cR@Wدby %-kgl.&c@@f+@l@@`b]PÆ2`&uCzCw iJO   @ ,jNMN9/r5mM)rʽP@0T d~$^%'z[6,  U(p ! d_trYdRhd?""w)CEN>/}O@@H ,Yw,6;51p26)T'w1@ ;^WɢvĤue'ZV##e?fOQ̄  *:\ɤ PghZ~&[5-݇kϡ^_@Ue   H`Qm{SL7wgE u⼭] 2?۽/]/I" y] O ַ*~`4S{}d<g/镭D77ԚN}gD@@2)P_SG]j:wi|b_1Etrwl@@L X* T1`q 0w_qNDZ@@; Β@@MɎum6l4u/e=vެ3q"i==:yQS=ɓ)   PuUez|2ɽKcqEqS3"e>]8 W%~][̙m6o&緽ՙ@@g9sb  @m+Vopr $B(ӑ^, hղȈ@@@w V)rMgwxܣݡ@C  z?L̝@_?) @@}~\@@`Uߐm=meeSIK}zLt@@@ FƜOT'% 9A6 @>x^%qhH|G`DijL  nͻO  @ D;70!>PmUA9(pcPo8+lQeFHH   N%zf mm +::2܇S܃;Fc  r?gh86빘/ Re'l@@\=w@@)0_S{׫cE9@iCcz}ip@@@\L{[X#΋'MEā㑯4dkx]G LSӓO~MD  d@   OFmVg*;s#EI^]E s3)   d\ T _i=M8^?U>8f:,y=/c  R]OKd6̥eo~t.e.@@2!@{&Y@@`^PÆ}kfy]]%0kNJu*UOE@@} DJN;b  3T+Ȱctmwƨ4R#͛ht  '@uF# \Etr?j粬ʫ !f$Ꜵy5}7ר|Fp   @vK⃍]D+rU=[ @AQ%Ie[߉lo   y1@@ L' 57b( siJhy(pap\|xJZ`?}n#!#  JՇW^yNT'_x9蔍q WX* t1:y~@`|ec@@&@{Y@@`>:FCWYsW iڹoV?Ӻ[2T^ 2G@@Tا>R1YL.;b  Jiaӵ2h-YOt71S3#  =欈  0Ht,P[ޅG'XkVcSp   @ߧN{SEFl]ۯjă0?{7C]g?3   ;;{A$     ش|`J DNeZF4sw@@@  YA8oz*r7+J-W^m:"(VhQ 3KT`[]?i#   ­M'e?i<0A`&z{WX}t- TV%@@@`^^|h.h%ǫ^4EK%E;c  P%kx>|X̓@$iOl1O3-  Y=[@  j[>f2&kOH~-=ƥtswS@@@ O,s;76(>1}wmjS/2@ 6]#^pK jڞ'#;7ʥ@@}E@@ gz'ϽR!g%O$.螛(PV<   &Y"<ЛVکN~΁ .bJ萦t+~gfzeC@@ gZ@@+Ք>Xd7 V/TCzeI/ժe5;E  %nmmoQkR^9$c PH \H ,H?FZr;RC@@`n(pGfA@3S侷r=/K,|ɩ~[':/5 J$rD@@-pݶZvk:t1XxYcR@"Ak8{oQe6   pҺ[trX|salRj_ωs @> Ufӵ}|wϚN>Ӳd~O  Nq5  @u흌^5n4=vku.}xR B>X+iÁ    JxoGL#n5ȝ~@0ܵYTX?N_mJd@@HC4  PKIW$%l l^_mM @@@n ?8jn_w8 s%%ź8Vgs.6*P[i͐@@p$@wGL B@p@4~:԰ ғӠ ܒ7yfG raT?*J-ݺV>/ܳ  XH/=Jlȍ/利tiVUGEeα@ X*VhQg7Vwml[&O@@'h  +ᶁ⥏3Vnq%IgL ijӳjGwTՁB    ܏v\R٨㋧Ř2trˎdATQO k*A,lPzwnL@@WS1@@ 잌IŊqgMy870m=oRZ"/@@@ՇW|Lw8!ij$M{ū"wp D2$;m|$ 2X{S^ @@;@@$dn- ?/ GT4s΃q{Xwd   s#,=ibǓ^:_/d  :/*$2!`[u}rz  @> POE  Y0E'izP XUzSRrJw^ U,   /޷܁ˣOurn∭b=PL'wp DA. hǡa N LUbHױc|D<  v @@G7vsG1Y 9rA+*bq9   |lm˴c_Rڝ<KRܝ1KP()}34=!0;N$Mw3}F@@Μ  ;ODϽ\1nG A CI팢ã}uJX5@@@su,ֶn%$lWQ@IDAT7C@Y rdHĬbV M:F#  > ݷd  0+&;nZᐙE<ةg.'NiRZV%ES;!  [[tir:xh\-R1@ Cs*sc3mHK2_~@@7<  WێW|mN>-YWC̹DB?֡uǪ*UWL   0s zK㩪u4fj;$c @YiaMN9RZ0 IoEZ7m^>"   ]$  0]e^=3TZV<  dZLOmhGm݊M:^~|JM1enQP˱Q 8zV嵟4}}>fgN@@ Y*B]rC@w}密F~Yzpd~)m5{:vyPK+A@@P^|QMF40켠2}pL5ܽI0@<jߩIg6mtg?y#  E?d@@ K`ÿ]3YVfdM߹OuՁ|@@@W &>;q!|Kۗx/ @z^ !ԮNP؞@ i\<)@@)@ww;Y# ̃@,v4|M'gUS"pMZDUi @@@ S%>*:}Qᾨe[e>U9c ^  ٤tDv?pѶ_lQ6gM@@ IBMrA@Ⱥ@|px{M0+J`b*>tjUWr3W=H@@rRYc:rȽ?f+XbN;c R/ҤPӑ!Ʀ]i@Y؟LXOݹ@#!@@ @DR@@-Xhsx܊h }~D?J|֮~nwrD@@XpٞC}j"T)t@cB:7@FlYݚ~?wID"  9  @,{f[ߚP+ \B,@EAٿޤMw-/H @@?wvvfML& ~i5a0 P@N_Ht/텼9۴%z_ ϯxJ  @n/7(@@ T n;Qpe!ZTiV L'\TwT2GKh   @ PMw,Gmvpt\M)-1 O]P.E' /A2s-/7%`D@@ (pϧ"V@@U7w]\k eG:íǵX7-C5Do*   @ ,YsPb3uæ=Uc7"@B1Q.ͻL=ִd-G.tF@@ O(pϓ"L@@:0mjXڴY{dS}> LM%7U< f@@@ *zyuS[U>n D(//S_HLJLw2DTۧP^'B  o{  @a D+mYOj +;)'Ĕ6֨['  @iO0Eg:=8iS*T\1N*HT"%|lzZD>L  {Ď  ōOFyA@{\NiE}VcQK   w^G/<بɩnNHuL{)p//S7!@C!}5#J?, I3-[b ,  (pwv,  @u흌~Zbyz湲9.c4>wu9ݲB . ]@@@ 7{֔kǧ%Ө4b_r"I@ ¨_|> e֓g-(bLL  (qL@@@^ nWx3{ܯ8TrjBQiI    0gwս7ik[ttO:whĴT]ns9fc 䘀땯4QȱDž?4oqO  dU8  Rlh'}X ͭ['ԒbZV-E@@w 4, k[{bcSFM-h̔U,e DrB  `ϴG],r"$p@e%4m@@ P={VF@@WHx4kx?EJo2-tpni&X@@p@me*9ԧ c-~`E>dMD}lXcD`a <ݴ -  dX   7X>jW/7y2!/v~w:X@@@ci]n~tb)07"@^%A<2iZgN#0%[[ef3@@)@l6  WvvU/C[:}U~ -kjɁ w'q?43%ˋ$k+!!$JOO'MK!@ mc;~а6-%HbKY/nY-ڷGIm3=Gǚ}<  0fΛںhFMVGY=#H{p4DlU,F3ɹ),{q!@@@`d(pgfA@@ ! m-4 "9 u1 ӳkj!ͬSɘa!@@@)`YuYr2ZZ8kA,)r+@`22BjКj댌|̈]Tg:s   f%ǝY@@[`n'C~ܝh0 |)ZKTD0K3<   Ϯֽ߮TgwbWčg9@m[Yݽё8@ڪ8?>vx@@^܇ޔ@@R{ *.cY}; ?f 6Pi{# @@@ ^)e{x=F]vI=2, 9@Վ^?تhgs87U- 9  I=IL  @"͇6;Z}K[K uzfA(QiQpMŸ   (*-NTի:b*<:MɎ7rh4C deep[jV[gtD ^zIXzx@@F^=Fޜ@@@D=3H֠3C,7    N}K7YӥY%eڌ B:6qJ1HJ ƆkE@@`qd@@FLmW?XB38S3o(p4bBs4   @B?ڴÖ7 @m+up(m?+'G^>fn|gR%/ϑgF@@=a2:   |CeI;>DsY#"C/n=_3#"@@Wxh  K/k.⛝i2L>ԗ>~n7]Pϲ']@@@`vp`Y~`BfbBp4Fz@dv,[SYpmeפ1&A   ;J@@@c-6\#i&Kt< o7hRqʋdYOx`iI@@RD l~˜If7v~8]cMKaާrIRckjV[W409g8l {_]KW@@pn_AG@@ -u:[mENpݦ d+p[Qy@m<)Jn@̎  xLŚ=e^\7=yAe>1A¡z,Umofvm FIpұ76T=3)0)   A@@HEf":Q o+-/R蜷   ֜wnlIh K3meVBn4Fm+u;͎n^KXzr&Wvm"  &[@@ 4Wo8[r͓N)05ė]"34|TPd@@R]0/;ָH7+n8"R\mmR/eK[}Cmg0y"  7   K C7f3 _Bt DcVҠ_G25$_ME/"  @ wMqIM-22xO ;;SZYm]VF<|aaoeF6   VBU@@@ɼl+S<K.xh}W%.τ@@@U{7UwObe,Uک #!muJ1Ihe}js#1s   @r=̎  ZrEO:{%k ;# L`IͬWA._>@@@fM,KJjK̎-R{4*_H=49ZFt[#"z#痎\TWl@@*^]YB@@ \k5MhN*`wq,W蜤   Nt_{^jJ(̠Y% u1$Ym2s~WpK$0=q t:\ig1"  `wo#Y   0 MeHv` B'FP`ˮ3Js44_#T   1tj8֮ǝ]oTjjqL;i GC&`)'7ODM=X"abpj?@"  G(pB  hrhNΌqbxhњhtN@Kg@@@*s ׼5SS)5 k}-6N@<|aaK   0 \mTD@@̌Z/pk?G/uWMVz{1   ڭ 7^kiX[VRD ''[F-E"" he}jsq  xV;]ZC@@ qC8ɘBy@F^T[g!}Vc2TV'ˢbW@@@E9y$3ǛNßFeY qw! @VfNvg5 8r~XEuU˶   nVX@@Ao5L9fe*-KC=   ~G*I3m儹p8hHwR4 t:\񄙑3  ->D  @Zmړ14Y@6ߞۧ\G9   o4[7Ihӎ#qWFcRSpE~H h6U4oGjfA`Hڪh   3KI"   @{C7jɺYx~n5kjYr3"  ͘0J6ת7b<;fi)?ӒE{j4C 1@V(+WuiO};'Gp,瑌L} +@8  )&%[A@@ U&,X2!=ivs1 K:_i;   @?O9nř+pQd+- ij @2Ֆm:3@mYZ\#!@@HY.'  %pzéC~7DVD@1/m;zrÖ(%ܿg@@@NHE]ұ6N?Q~C``99}lz!DG/+j$   $`@@T([plS/cũ @AnH_i(t6   1G_=2>̗jiɽ0[qdѨv׶Y~EU+W   _Z76e/箬Nܚc_#yv]~FUOecª(Γm @@@~lT<:KUԟ.x[Nkudy\Bx "pV k:sֶD ڪϦp  )&[A@@m,rS>m/+0y|K5 JZ@@@[էQzŽo74z՗ j?Ho1OQ)HP}Yٝ1  )0  `VZw,9qSĊ;fM,ͽB c@@@mmݺەZmϟA8]Ql+;ĭsYq_}6>H|-hݛ#  @2ɜ@@@_m=IWmLV9Ȍ,&pT:WjU6&qy, .: _@@O LLQrǍĤ=W|ڴ@GYZFt*fp R Y=^|ڥ96  WR`@@/ ^}('Xwy)/rw].,ݟ/F@@8mm=Zc:f9&ŗh%FcCAu+CvRo'?8p Z z@@H Sa@@ -Cf <)L`%EZ|Bw%O   @M':XM;fAK3meFc\+WcYKak `vmNI~A@@!D_"   Toܞ;T>TO!'p@uc~~mwXGgdl &D@@@`d2uuS˻=yoTjjuNSqu`vw@c1ŚjbsGC:7;G@@T=VX@@@ -6Kބ+]mkI@Zi#*1w5@@C6_{5:oR*_Sw_z8osԷu~ŷgaFMa=Oa{H.`.CVqE@@`   (wT'tRs煺pzqJGP   @2j_AK(ŶBi u1)'LO ߇=R. Bd)A@@F܇ƑQ@@@-6h<3st4)/E}&gklavL   H et继xs'PZe-e&p6E+ 8QOt@@RW+B6D  gJ.Ķc?1 l$K[).Ҭc}?I#   Ǫ}w֪T'pT^hs iiBYhvlob_1"L@iuU+NM@@@   h8jF472pzK)=鳻肬`@@@d ̚8J﹪B6dKwatJ]RA GV==#Gwiw]z#>+l*?"sSÚ/<   E"`+  KyΌ MW;/ӊw"FG@@\"իZ EL3lfp{3!8@)lO3;oݢľ`XcpF%ڪ?3񩍡1e@@Sㄢ   @ uf78 }جk,6<Ԍukllʢ# +   *n3Q9!5bLFcRScvq(rOq}m߭]ꉘ(U f/Yb"@@pݲRĉ  J.ĶO~%E \s8-B]yAlS  YM7Vh[B ٖGr}f5cE!Z%U+4 L@@@5$@@<#Zq  x%>ڦv=jּRQ!QK@@|+P<:Kw\7UTuCK6N4><7 ,nvlf蕃]YӮ^vl0&SVT?DE/J @@|#,5"  .Eh]- ?)_K>t_6Q`Zi  xL`^?+0&U-OR&p(fhE  @cOqGx`8@@@V ~G@@hpki6A7?TP 4/_T  rv.UW+*dYsˍFLa{nׁF=-\*Ǝn[9O   Q.xtaI @@/ .b'tR#&PQ>tzrBo;@@@GNt3X;'nfb[ dgeDW@mVs KKcˎ@@@l %$@@/RDˡE?ΛqDvsvݗ6 {cˊr҂@@@㺩]Mq#FV[+v@ ,j?֥'G[ 8zJsn\kW֏x@@Ww|Ф  WuwE b?aw=^ɉ<O v< ti]_3G@@<) *$_Ims n4@nnEFM|} ]'-yrL}$  "Z@@xS??375AA<}*t@@@Vן\<-(J5m%`)mվ6Np,}o@@@R__#"D@@~*.)Hm4i<%ԟ.OF2   @&w(6i4eO[f\TmSR'W |j@@@m\qۊ/  So7;=̉1&faHQP(NSEIAFIX   vp7Ͼ!lj4vij2;+SڲYh/LHTM1 + գ=  tÁ  xSt&+wdu&=:vEcCWs<  W -`+uޤB}Aݑ:裭6A\n(77GMZUuGԷLױuSiNlI@@ p+L~   pZdmz<*+ԕ+`@8@@@/ 9ѡԺ 4Rߟ-^6#+G;j;Uԙ'p|(?7SP>|Ό  I`$35  @M[(a^{3\ٙ _ߪ_}@6WkTvƏUzRcu@@+5UP@V,-{ґGi0E]ejuvjWK{|;G,@2La{V?9q07   0\naG@@8.뮔 Aa-y @@^=Z M4*q,pBn8;+SiڲzlT=\Ό Z+%H@@@`   ^hq$fA ^ h*7>Y*/  @GWD_uU{$P4ӊlgr+59h)77[GPkBkxa9, =|wn֙@@H!ʤb   @g8C6|̈@{Nog{@@p/_2_`iB3F q P83[k:Us/ l'j+⥤@@G1(@@

wm}$:ɒ q̜>x\6QY8z@@H]6ڼHBAf,M7g(!DCAڲ|7E5Kǵv>/WW@@@   3l3Z-wr'](ŦֹUZ6<  A 8z^ʟH4wȶ:yqZ-ٙ:Ж͊DUO֭Y@@@\;  Qd_ȷ,9;cD-ݾ`xfHK >|2  G*Uw5|Fg[2Vy1PK99>ѮVp iɑ`}ՊU  n_   @ W9?6<.6nP75Camb#˟8|aWю   %'a@@@ȉ>n0A-}ݓutьS/7!  5ںhB-M7Y:i}͊D_L$ p\5,-@@@q~@@@Xe +QfjYq  \>s>Y{ee}@   .H4oe}?^Q9Sm@ pVwᎸ iqB Qޤ  $,  ~`I~|͒ic* y!h$?cD@@RM`ݿJ m)pl vdef3{[@\:+NL{q妸:@@>>   \t}Wmݞ0#\8Q艀lSqȍuьbY"d  Z{'^ާ6sW(M:V~Qͱvִ&W )KCcSO h  ~?.#  ,XV;yc9_1e ̀{gkTD@@|! 7ѓ [giho @$Bc&`>򣞞>w;"  }G@@@ɼVێA@@>zT}`7~E@@%p[_:ϋ !nyxÓkvk?EEf[lMgi)@@@xY   0 ,0˽Bry  \0y>Ywݚq#  ~Ot+LKHOOS05?.K1z`*FfHFA@@)K)\(F@@ ,XV6kwQ䄊@>zI9y\`b@@@?c:59Ai8[9awܚTKw^עsIxoV$`y    *Jy@@H@ɂP _I_pԜKʕŮnY7D@@/OWӗXS \Z`V*n۶spַ%.lj]U>w }@@@`H(RNC@@XdKl;Ҝ 'OonvO_3G@@F\DzzasmBsg~7=3#V& @Ư/\c[>   09)"  gJ-Yַ3IW`E4S\TP8  /ߣߠ[nЖ2e粔Qm?ت DE% kלV    $IA@@k:'SW"FJ ?;O6SfiH@IDAT@@WX\F/Zo3 Y6Rߟ#qCAYz]GNvĔ́KuUk   j#s$ղ&@@@ *K0nJr(LkDzt]ua2|^ĵ I  xD৿٥FuƝQ};Xòe&Q㩘U(e@|-jfu|:\ @@6 ܇@@@s _f+WnM9wkZ vu, Zg29@@#z5Z1 vs/۷YY@N_pP4F_=Pjٽ@@@` HE<   ,ZjUۗLzYL1Ko9kT^D   [~Ə6;֧{n~֖g>pf69l~f(E >펭%W_sZ!   @    {w?^?G@A ,}StIdV   @5Ƒznle떓S^ߪh|h svT4O7#   H @@>͉9J3E7 ##QmtXZ@@.зަot(>] mvn85?ޕ< M']w5{ R@@@\-p+"N@@@yg`OX&*r=s/,ҟ-/,U^NFjE   yt?Viޣ 嚛nt\#P٭m[yxrپ   C!@P(2   0LW?JWR1S4 ";PzO-s'i  #  0}yj٫={l>(SZzt7~4Ds X۹~Ւ   #+@z3   0 ҹ] Y!K4@_ Emrێ   P>%f7p }6@O=M   }ptC@@ `w̛ I)WCOU)+3|I@@#ƴ=%'fE+G5/}2F@@#@{֊H@@@|,U=c}Qr`AHן2]7^3Aeq e@@;ZՀ 8%+*y@@@`(;;  $A`uwZ/"ϚӒS" y52U"gD@@hկ_:\O5*swBFG;N5/#  }kF   J;l/ S .,QC>>"  w"Vm=]ZuD,!|P؆ꩧ@@@=-"  ),P91yĄxE IhxB[L+7;쉼H@@^`ӎzr~Nvo1 w}k~ ~G@@pY+"E@@|üO B}ɺ hF¡ Ho@@pޚSf5Tw.x 8Uj~K   P O#  nhqZ8%'͹;nc7OW뼩E \zq+  8rC]΃8 VCS@@@.k /!  ݅V4%9یŶ҃? ҟ2C/-Ԋ )  EW^wPOQ#N/XM4z*ꅌ@@@M y%   >(wTevK ћkEe?/UH" @@DbZrڟTHiC #Z}mƕ-C0C   )    @{#ᜇ@ ƫ0?A@@ؼg`)pjW@@~\urF@@ Xe`raN;#,m}&'钙'+E@@~ԜUUUwV</`j+2s1   @(pO 0?   $){ !?Ia0-P;5Iﹲt{^N.   $]'+K{NN%) E f9j׬|_z#0S   Pw.!#  wÃY]Ȳ>gb(HV̋+vsan.Ysw  .xmE^W#lgW,SS9N    P @@@ *hR_⩣u&YE6qA!  Ĵnk~TchCHAc=T)ii,#$@@@=p@@@7 LqQrwM+~(^7Iל_SN3# @s[^\m8Wћ)`5Y㣾cT@@@Wxh   Я@D_6 >f~_)N zYi}D]wY.^|!V@@ ioMA{_QG^db6·<.΃@@@$@{@@@ɂ{f+A/GNx]6OyeutF*Əz  B9zwiOI9%JwG^n"'X@@@=`@@@o[|m/9Y Ouu%>q|A?_ d  @OoT/֠LAsjtcx&bT /լ]v`$&c@@@o P%;@@@ eύy9'09 Z 7{\:^L-RVfpc2  NvyS j\+8IoXU@   )'@{- !  JsԷ.\v$lzm]SKfilaH@@ ^ ͦ_~㈢1'ޮC03zzt}HX   .ŋG   矼ݒ&h@lN-Oei1 9@@<.L!{Vm=SSriUk*Od   08!  ~X4,vKK%䏀r3 {QfOy^K|@@ p]MA{uZzZ{Њ@p[ʵ98   k(pwR(   -):ʎl@O2W.1Z\[Ic  HL5j)h|Vj٥J 򅾝ڿ\_b @@@IaeX@@@`VfG=>3Z!V46B..Ʃ(߭7  KƖ;W\+KF/7T|k9@@@9 GΚ@@@"Pr2Ч,iVtB̨O峊4kXef=i  hC{N} qGp3R&=@@@ U(pOՕ!.@@@Wߟ`&1?%>e m|'L hU2]4}*΀@@TSsiBXĀ.`m<\foT7   Mp@@@ i|,u> 4i01$EtlX8I^P(/'#)q0)  u^Zkp)]՘Va23^J\@@9^{Lk6hZIHlg"He/|MD̲   @@@@\!pyKcNxȼ E f+J͞guc6h -`xOnpG@@Q G@@@(`_pͦjs$ rt]wI=N3'jJhe]# p D1mwt!{_Q{N};s 0Ӗzmc[П.   BwW,A"   @ɼ{BwC9.c!?l[|]wY.:ZSGila?1@٫M&S̾ڞ  F bY;fá51   (pw*#   ]<+je }X&aP@Ÿl-L*-WR/_ $ y۵o1?O(3,s 2C|?>ҸC@@@pwܱND   0,.co$L=S14T # iƄQ8@cFύO_ bԭ=}Cê?ڦ9@-]~6 ݸ   ;(pw:%   LcֱtnL cr Wi5@L{VF" @ 4k=G&:zS/P"BXM,O^G" @@@=a2:   x]3`cM/!@j,]={a|(S#@@@-= _5?[UӉgMz K mYrz[3b"@@@)@{2@@@ 30H|_xQgޣ;}n$DBBOJi[+홡2믞ǚnI5v GOG}{?ٗyP_Z{Jo7-x @ @H@8F @!n/KNSGq  0gz+"$_$Zh @`?|f_/{lҸ8YS_v8\G @I@}V[ @ 0g_}%ߏ HXl4mN8/B"׮\Z-b E 019~w?}6Kӳp7C @`Fr*xȈ!@ @" > L @]}f֑J.7Ɲ݌NXzp%H6n4:24s718gteo:wG}R~l4]>! @>[3"@ @袱y/?}ab PUMHhUzeKҚEXE 0u)'?ڑ03}+ @`.~Xr8{{ @ @~pוU @bjצwx2nOXt~zV_>oyzK׭Z֜8-^8:#WTOf#[ )r{uJc`J @ 0kt @ z N5~E$7ugst9}MW.J#Vz @/LMpϥvxO>{(EvdI/RU߼[/E @t{y @ @@ lnM)H@ TUJ]}te35|_Fg؛ {'о ?9 {?,6o۾I @ @oN@ @污T%]_rpzW`9=m`y:otimW( nafN>xjKvdq]ٛ&AAPr7w=xA[ @ @T *y%@ @!!SI<^A!@ rʅiӅ+/m+UOK+O_ZJn̉vtao:¡9 @"7kw4Vw͉ @{? @ @_5uɜ'b+z&@, .ܸ4]rފt%iêEiՊҪXvZ}8Oy?xthb8!|SouUI @KO~k=̆ @\`ݥ7ӿ[ruCJP>)0:/~˲tΚi7]ߗ/L/Yp@~(jx/^%Bڱ419O媅3-LV٧fzp @ @/ ~V$@ @s)^r!K}"@//I`Ei㙋btzl#jU}Q",v]Ү^N?|49, @M Tؾoz8 @ @oZ4 @ @ o)uޚJxi VN[zv}Qt~_V !?TI{H'&ބ׷7]vű+0=KiM6~ @ -ݲA @!p.^S9&@7!0|}y>6K tN{!&ʶC )*p$FjݼޛgN'@ @NZ@\H @N-VV1N,ܖ-0ܪ[/Noݸ4:/+OK6?6@) phV#~4_ڟ9Rju pv{[) @ @% ~\LN"@ @)oun:$Y @`06/aIڸ::<-Z6_x4-/]mlOT @;[R @ @ Wӭߋ"?o L^ ~&09#M'% /YA7[s{q͹t֟~9T7Aϝ;s= @?i?ٚ @ ~"& @ @^NL9(D[^MS'@+M} 뗦 g.O_<]GҢl `W.L``?x$B;& w/_7@NTڇ՞o1{ @ 0'@ @>X٧֤l]R^ǥ* 0^_raZ}tFt_h ~7A^wGRCv{H:‘x9+'^~ݜɩv` 0{G7Uiv=p?m|eiL @L@}Ψ݈ @"cCkʳRD<- @["/׬8-Pda~86:N?N b7ԭW LMBtLoMf?+G_OCq]BI./svǔ̂ @)?О)I @ @8뽟:\R) @@NP:;㏆uM(~PnVso=C 7';KS//=ބ_^t:41ɩ; 3 U򕪪nyﲿIi_,M @! ~ o @ @~袱y/,?;dskT..IOk6?7]䛮_ȼV Pu44[UNUEsuV۪⹕cCL?yS^ ^s*qޑv/nc/ϝxoOuv? ?wh2#:NB ?SM۟8\B @{- @ @87\خJhD#@"Є{vy0|JG#og/_~O {j)aV4׵۱ua @?s|g @ @UH @K`ͦ}".I @ @`,ܰF"@ @zI@V\  @ @ lӛ쿈[.ۺ  @ @\?Ł$P4 @ 𪀀v @ @7 suK&G'#~ @T/Wܶ[6ƈ @ @4 @ @q N9;kB @(x. j۷ݶozM @pg @8!3zF>Khw ]d @D(ΔڶADP3 @ p|, @ @>v>0|8^8B @,Ϗ;WA @ @ S  @ @_,qGG.X8gQ @?+]~/V} @ @ a̛ @ VJN 4  @ 0u|kJ_&lU @ @! >$@ @_QG=, @ W?))!OwoeW_U @ @ 2z7&@ @#,:9\ @;%/*}a׽Hi,{ @ @fN@},D @hI5qS @ @T w)/ϟ? @ п*#@ @-eKk3#vLv^wO @ 0pOvn?7p+ @8%M  @ @ ևb'@ @9SR%. @ @p? @ @@w |;r)t/-ٙ  @Ujw~=Z @ @p52C @ 0D[.HE @ @`TJM[ @ p|, @ @ z'VgN)_z  @uTrO̟ @/ZO @ @p甪uN6.H @VNv{}33Q @ @3/ >F$@ @F |s)սnr] @ %T%Q/֟Ƭ͒ @tA~ @ cպ"9iKl,E @ @\=S0$@ @t{, @ @])eKk3kHP*Ww0KP @ @`n|kn(Nmst_!@ @ $|gN;wnFd= @ @NF@d\C @8N5W]ATi @tKNwv:o;1]_33$@ @z@@  @ @pԭ?U @@ |/bwV֝;QP@-b  @ @p e @ @A`oC?mei`% @.x4~ӝJ_sg%s2  @ @@ i. @ @^8V[oR>S^y @S̭߸'}Z @ @]) ޕbR @ @ et׫R} kB @P`"JWw}sxo"@ @xk0 @ @L KA)_ @^Joyzs'@ @􋀀{: @ @X٧֔oT>PR& E+ @ @J @xE&@ @9̩2aRsz_s'9@ @^T]t?8>>6y˜A @ 0($@ @̘XvsK}mּ6tE =oƆ7 @@ 3~4ܾg={ T @ @ Ϝ @ @)uҺ}Ӆ h @W\>{϶[oDv @ @p?3 @ @N@`{p}+MF}b&.=.w* @@ ?9w*ݽM^fL @ ݰ @ @W-[Z^Tʵz{(u_U @`vſVtwn߳=;@+ @ @`g @ @ظzɯ\o.Kc̓ @\]rm0a @ @ 03i4 @ @8[G~ Nོ'.x?C @fIoc/UU/~o{ @ @R!@ @8s=&>T  @ \I*rQNN66=u+ @ @ܻlAL @ @gM}pR+#]eψ @NV`_S+h}4V`#@ @ 0S3%i @ @Y袱yV{W[WT6LJ[M}  @=._|JG.@{/ @ @T@OVY @ @AޗӕAaHK`wt_]~= @ @t{w @ @M ]WR7RL97N%@}Dzoxh4o @ @ + @ [;m4x]R>V  0Gz=᪺oBj$@ @_c @ @F`{>rȯN=]rx{kNK6УErsz`[vu$͇ @ @Lτ1 @ @zV`Ӧ놟Y(;s٣T=8{t/NAaJ @ @ pLc9 @ @`κܾ46omdK̉DIJ]߸iM @ @t{.) @ @t:21Q=UtxZz_]4 @GyF=?SϬc=2w$@ @ 0N @ @(>K^ri6܏8iCqj`{j?}ң @ @"+ @ @`]zztꝭR]Z)>xw=o @@ CR==K_jwL @ @@ w  @ @@ suK&\벩&^Zy @x.^}x~(io3 @ @ @O @ @N^wo]|dzG#c bYɳ̪@IڣŵG#۷D4 @ @ @`&l&5E @ @$V\E )r⃻_|~w.!@oF ?_D{V}⁛|3c @ @r& @ @98c/Q=Ǎ Ws6 7"@-t_Q|&]XNu'هKk|;Rv&F @ @@ wݒ @ @g]qÙ\ί{mPU {IQcE+؞m8#{_ @ @̭z @ @شNX|AU{{#k^s]6ERhy{nxf܅ @ @ + @ @@w 3UZu#rΎ @stbbh/?KyL @ @ @,"  @ @޺xbU缱Jyc|yvU-} BMWv!A#v @ @!? @ @ @@ suK&?+紱zc.st_atL~GTsّدʎv~B'^ZS'@ @ 0@ @ @Q`NYMf|vTG=)Ƕ86*ɔJ׫%9Gp.;UQ4ڵɹ/ @ @fS@}6uM @ @@O t齊h 5Q̚[|Ln]#a&^Ҏ{U'Ҏ޳왔n9 @ @ @`Z @ @?8jV:UZSjMα_"l~ʫء~(X @|&G8썳F'7ث;<|K5 @ @ 0J&@ @ @`ƪ3xvyk(7Az5ݚ|!&V֚Y'*K[7[w8MݞVKƦOtt @ @ @fa @ @F`˖g8c=oMnED=w:G"Ͷ<+C?5 }&0#"#U{yzH}۾8g+ @ @]+ ޵Kcb @ @k[:B= @ @ @ܻi5̅ @ @#PT{#JǛ|nvLǨQ9zz\EH=Ki)WKvkV_ ty1&A @ @=. h @ @5-[ZXpbr2J[ZTWiq.yQiqt_s~Y>wE{ű߄< /E( 4~r(*^s<aT?jXwفn( @ @ @ :{w&@ @ @lژ[89] WuY\v',_5!&,_Eu]F"(=8:#8ǪW7ݎ JrloN D =Pn^GP=PKnCx9/PUjԇ^j |1aA @ @3 > @ @ @>rp^hUxnB|⣩.#MΏn_u\; :qTTy*syc?ދ9u=ժZu?ı<՞㼩Vkjtũiju÷OǪİ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ƎBȀ 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0`d ;IDAT 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0`o OͫTI]IENDB`rvirding-luerl-772b25e/rebar.lock0000664000232200023220000000000415114130670017323 0ustar debalancedebalance[]. rvirding-luerl-772b25e/doc_legacy/0000775000232200023220000000000015114130670017455 5ustar debalancedebalancervirding-luerl-772b25e/doc_legacy/luerl.txt0000664000232200023220000002365615114130670021355 0ustar debalancedebalanceluerl(3) Library Functions Manual luerl(3) Name luerl - The basic interface to the Luerl system Interface functions - New Version The Lua State parameter is the state of a Lua VM instance. It must be created with the luerl:init() call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key _G which references the top-level table. Note that Lua Chunks (see definition below) can travel between differ‐ ent States. They are precompiled bits of code, independent of State. That you can ‘carry around’ this is no unique to Luerl but a low-level implementation detail of the standard Lua language ⟨https://lua.org⟩, for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ ual.html#3.3.2⟩ the official Lua 5.3 reference manual ⟨https://www.lua.org/manual/5.3/manual.html⟩. Spec Definitions Binary means an Erlang binary string. Chunks means a portion of precompiled bytecode. State means a Lua State, this is a Lua VM instance. Path means a file system path and file name. KeyPath means an Erlang list of atoms representing nested names, e.g. [table,pack] for table.pack. Keys means Lua table keys, the keys of a key-value structure. CompileOptions means a list of compiler options. Currently supported options are ‘return’, which returns the errors and warnings, and ‘re‐ port’ which will log the errors and warnings. LuaCallReturn = {ok, Result, State} | {lua_error, Error, State} This is the return value from evaluating a Lua call. Functions luerl:init() -> State Get a new Lua State = a fresh Lua VM instance. luerl:gc(State) -> State Runs the garbage collector on a state and returns the new state. luerl:load(String|Binary[, CompileOptions], State) -> {ok, Function, State} | CompileError Parse a Lua chunk as string or binary, and return a compiled chunk (‘form’). luerl:loadfile(FileName[, CompileOptions], State) -> {ok, Function, State} | CompileError Parse a Lua file, and return a compiled chunk (‘form’). luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason} Search Path until the file FileName is found. Parse the file and re‐ turn a compiled chunk (‘form’). If Path is not given then the path de‐ fined in the environment variable LUA_LOAD_PATH is used. luerl:load_module(KeyPath, ErlangModule, State) -> State Load ErlangModule and install its table at KeyPath which is NOT en‐ coded. luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) -> State Load ErlangModule and install its table at KeyPath which is encoded. luerl:do(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError Evaluate a Lua expression and return its result which is NOT decoded, and the new Lua State. luerl:do_dec(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError Evaluate a Lua expression and return its result which is automatically decoded, and the new Lua State. luerl:dofile(Path, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError Load and execute the Lua code in the file and return its result which is NOT decoded, and the new Lua State. Equivalent to doing luerl:do(“return dofile(‘FileName’)”). luerl:dofile_dec(Path[, State]) -> {ok, Result, NewState} | {lua_error, Er‐ ror, State} | CompileError Load and execute the Lua code in the file and return its result which is automatically decoded, and the new Lua State. luerl:call(FuncRef, ArgRefs, State) -> {ok, Result, State} luerl:call_chunk(FuncRef, ArgRefs, State) -> {ok, Result, State} | {lua_er‐ ror, Error, State} Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. luerl:call_function(FuncRef | FuncPath, ArgRefs, State] -> {ok, Result, State} | {lua_error, Error, State} Call a function already defined in the state. Result is NOT decoded. luerl:call_function_enc(KeyPath, Args, State) -> {ok, Result, State} | {lua_error, Error, State} Call a function already defined in the state. KeyPath is a list of keys to the function. KeyPath and Args are automatically encoded, while Result is NOT decoded. luerl:call_function_dec(KeyPath, Args, State) -> {ok, Result, State} | {lua_error, Error, State} Call a function already defined in the state. KeyPath is a list of keys to the function. KeyPath and Args are automatically encoded and Result is automatically decoded. luerl:call_method(ObjRef, Method, ArgRefs, State) -> {ok, Result, State} | {lua_error, Error, State} Call a method already defined in the state. Result is NOT decoded. luerl:call_method_enc(KeyPath, Method, Args, State) -> {ok, Result, State} | {lua_error, Error, State} Call a method already defined in the state. KeyPath is a list of keys to the method. KeyPath, Method and Args are automatically encoded, while Result is NOT decoded. luerl:call_method_dec(KeyPath, Method, Args, State) -> {ok, Result, State} | {lua_error, Error, State} Call a method already defined in the state. KeyPath is a list of keys to the method. KeyPath, Method and Args are automatically encoded and Result is automatically decoded. luerl:get_table_keys(KeyPath, State) -> {ok, Result, State} | {lua_error, Error, State} Gets a value inside the Lua state. KeyPath and Result are NOT en‐ coded/decoded. luerl:get_table_keys_dec(KeyPath, State) -> {ok, Result, State} | {lua_er‐ ror, Error, State} Gets a value inside the Lua state. KeyPath is automatically encoded and Result is decoded. luerl:set_table_keys(KeyPath, Value, State) -> {ok,State} | {lua_error, Er‐ ror, State} Sets a value inside the Lua state. KeyPath and Value are NOT encoded. luerl:set_table_keys_dec(KeyPath, Value, State) -> {ok, Result, State} | {lua_error, Error, State} Sets a value inside the Lua state. KeyPath and Value are automatically encoded and Result is decoded. luerl:get_table_key(Table, Key, State) -> {ok, Result, State} | {lua_error, Error, State} Gets the value of a key in a table. Table and Key are NOT encoded and Result is NOT decoded. luerl:set_table_key(Table, Key, Value, State) -> {ok, State} | {lua_error, Error, State} Sets the value of a key in a table. Table, Key and Value are NOT en‐ coded. luerl:get_stacktrace(State) -> [{FuncName,{file,FileName},{line,Line}}] Return a stack trace of the current call stack in the state. luerl:encode(Term, State) -> {LuerlTerm,State} Encode the Erlang representation of a term into Luerl form updating the state when necessary. luerl:encode_list([Term], State) -> {[LuerlTerm],State} Encode a list of Erlang term representations into a list of Luerl forms updating the state when necessary. luerl:decode(LuerlTerm, State) -> Term Decode a term in the Luerl form into its Erlang representation. luerl:decode_list([LuerlTerm], State) -> [Term] Decode a list of Luerl terms into a list of Erlang representations. luerl:put_private(Key, Term, State) -> State. Puts a private value under key that is not exposed to the runtime. luerl:get_private(Key, State) -> Term. Get a private value for the given key. luerl:delete_private(Key, State) -> Term. Deletes the private value for the given key. Passing String in the Erlang Shell Here we are going to look at passing in command strings into the luerl:do/2 function, especially when these strings contain Lua strings. The problem is to make sure that the strings in the Lua commands are processes correctly. First just just doing it from the standard Lua shell as the source of truth: >return "aéb\235c" aéb?c >string.byte("aéb\235c", 1, 20) 97 195 169 98 235 99 Now doing it from Erlang where I need to use \\ to get a \ into the string: 1> St = luerl:init(), ok. ok 2> f(S), S = "return 'aéb\\235c'". [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] 3> f(R), {ok,R,_} = luerl:do(S, St), io:write(R). [<<97,195,169,98,235,99>>] Now doing it from Elixir using the ~C sigil to get the handling of the string right: iex(1)> st = Luerl.init(); :ok :ok iex(4)> s = ~C"return 'aéb\235c'" [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] iex(3)> {ok,r,_} = Luerl.do(st, s); r [<<97, 195, 169, 98, 235, 99>>] So it works as expected. We use io:write to write out the characters in the binary without any parsing or trying to be smart. AUTHORS Jean Chassoul, Robert Virding. 2018-2024 luerl(3) rvirding-luerl-772b25e/doc_legacy/luerl_sandbox.txt0000664000232200023220000000741015114130670023061 0ustar debalancedebalanceluerl_sandbox(3) Library Functions Manual luerl_sandbox(3) Name luerl_sandbox - Fuctions for sandboxing Luerl evaluation Interface Functions The Lua State parameter is the state of a Lua VM instance. It must be created with the luerl:init() call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key _G which references the top-level table. Note that Lua Chunks (see definition below) can travel between differ‐ ent States. They are precompiled bits of code, independent of State. That you can ‘carry around’ this is no unique to Luerl but a low-level implementation detail of the standard Lua language ⟨https://lua.org⟩, for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ ual.html#3.3.2⟩ the official Lua 5.3 reference manual ⟨https://www.lua.org/manual/5.3/manual.html⟩. Spec Definitions Binary means an Erlang binary string. Chunks means a portion of precompiled bytecode. State means a Lua State, this is a Lua VM instance. Path means a file system path and file name. KeyPath means an Erlang list of atoms representing nested names, e.g. [table,pack] for table.pack. Keys means Lua table keys, the keys of a key-value structure. Functions init() -> State. init([ State | TablePaths]) -> State. init(State, TablePaths) -> State. Create a new sandboxed state. If a state is given as an argument then that state will be used otherwise a new default be be generated. TablePaths is a list of paths to functions which will be blocked. If none is given then the default list will be used. run(String | Binary) -> {Result, State} | {error, Reason}. run(String | Binary, State) -> {Result, State} | {error, Reason}. run(String | Binary, Flags, State) -> {Result, State} | {error, Reason}. Spawn a new process which runs the string String in State where the de‐ fault sandbox state will be used if none is given. Flags is a map or keyword list which can contain the following fields #{max_time => MaxTime, max_reductions => MaxReds, spawn_opts => SpawnOpts} MaxReds limits the number of reductions and MaxTime (default 100 msecs) the time to run the string, SpawnOpts are spawn options to the process running the evaluation. run(String | Binary) -> {Result, State} | {error, Reason}. run(String | Binary, State) -> {Result, State} | {error, Reason}. run(String | Binary, State, [ MaxReds | Flags ]) -> {Result, State} | {er‐ ror, Reason}. run(String | Binary, State, MaxReds, Flags) -> {Result, State} | {error, Reason}. run(String | Binary, State, MaxReds, Flags, Timeout) -> {Result, State} | {error, Reason}. This is the old interface to run. It still works but the new interface is recommended. Spawn a new process which runs the string String in State where the default sandbox state will be used if none is given. MaxReds limits the number of reductions and TimeOut (default 100 msecs) the time to run the string, Flags are spawn options to the process run‐ ning the evaluation. AUTHORS Robert Virding. 2023 luerl_sandbox(3) rvirding-luerl-772b25e/doc_legacy/pdf/0000775000232200023220000000000015114130670020226 5ustar debalancedebalancervirding-luerl-772b25e/doc_legacy/pdf/luerl_sandbox.pdf0000664000232200023220000010332615114130670023567 0ustar debalancedebalance%PDF-1.5 % 23 0 obj <> stream xڝYn$7 `"@.@U)M$8 h$H~l3[_oϧ}n6{ytJ]v*贽jᦵ[S{y/^~|YK]ye[^~h7cTꎠ7]V9߫c_.2(sUmʦ] + {((8e]h:bHe aaura`"cB듭`TG@GxX؈}byʔ_|vL'GŞ;$3\e%PU:.6:%[=9SE+lPb߾gd8"m#+D"Zh&_,_G58N++6o{:l1ֿ8$3U7_|™LJvײ|Cf6Wx@ Z£>+09[f%`_'#Jef )8F4h@#.;%h0GPyM|8[*wZ3;:%qAͻ3d3=)qbWg%];e~(%nz94?'uԳ("uK1M~~33#ғZzh3m[O_'?7Owpkp?>hgG𓞏oCtlZqEzl*\Ch_71/#>}]L5*dVuv{`:50Ts&F$ygm;o8&]Vaȳș;uJ$Yڡ*c!cҘc1&dqBW;&NYAnΆCR4hZYϬDv2K"#{<\LVJӡ1"f[km7K 49Bܝz1&GHF+S A\V#cZ;'TLnTo kDy[B#>QPN s܉bJFJ2PkA&ESh6{;oGT|#ÂVP?p M2q_0)+y'!p]fΑ~Q=N$6>+l*yg쯸&I,W%ϳzJS{SPGUE*Y[I۾~ڿr+W6O7BmOa0sts9n&9%YÁݾ_"c8lz. >㶉)粝5Oane;p ^lC'Hk\)>dӤf}t8;ub"ǝ+4bۥS@!g@y9rǫLce>Z&CY;8/t.fJ%ow Xltd ݅!\8N~~]+UDƺ EDh)Lhq⌂)&F|Svy/ensJٍj+Iԝ<P%{| P+j<-:[?}jC6.W#8?N茹qg\E> KO+y)! +R.ZŽP\_8 ~\V8ed8:Nr(جGlG˭ k /TJ>Q\6|J?򲸖SgDvHʘgr"Ͻ^<7G:ڹ©w}:Jß6xY-eF`^/aS endstream endobj 28 0 obj <> stream xɮ4WU^w@$nH8LOӜ8.N0 \E-/?7ޖ2R"}J8뗋zۋh_ޤ~ Rc^e*,WBYqc|G`c_/Lt|Yk{~c0a;8eL]yv{4}Rr$$B*}K+ɋ@Ī`V1U;vƸ-G+GƧ9m; nl礏^((GEM.{/(拯qv߳C+ =o1.nrmv-d>is\etLEP4!Φx_<#g, ke۪߅ t똙F.ƶ:܎!uٮ{On{#0UI UA [Qm$%tZ Sߦ'(aU3“"t< Xp.GP)dLrL~^xt/S=ש';U^xo[USnR k֍I*wZU dsD|QU7Az V^ճ5TKr)31KHB>[ r3)ӋMw !&*tFpJN|'> stream x]Qk0wE;FhDuir@!CLN—y^/}Hy #i~2 Z<!ȽyǴ?wLt}v3^J F_H׏aRMO#ğ0YDmZ۩'ЁI㽀A3,WNr^J;1ˌ  ͭJ) Qf*P{ԇSLQ%*v N)KBvN"bU m]Koy;d|nn8um{,_{ endstream endobj 73 0 obj <> stream x]_k0w?E7F_k) uir@MBԇ~] c]Z,qF40Vi`0n) J^=Ax> stream x]k +> stream x]݊@}aI ,dۯݧ |SNeZgc{36}k^{p;4Sf csdzO{7Us:{d Wm]F$˵6{vپY߻+{9ǸrOӷYE Ǒ^;v65d-O6u*ǵpY&yA" *ځ֠ E$JP=)縧@{D{dG\r:IzD H2:򔤷4IId--$u"TP]u zZ+d A!3<rQ4tLCOz>ht_N'SL82b,F#+P.Dr  䒣fK}ބ|]xөsw&:Od|Ҡu zp~u{~8qxÜ>8[ endstream endobj 76 0 obj <> stream x]Mk@໿b-%kDHB iKLڳqTj}%.(<85nweT]V C؅t|D[*١UG=m8تXO1A ROMF$qsc~0BӷO#ȴ^Yiwoԑ礩-cm&S+97%8R~4oet?=/I[-9*@fBZα5#A+fmʡwЇ ( =q%|k<P%":f5=,ÞZ!ݘlfl5} endstream endobj 77 0 obj <> stream x]Ao@=i !DۦGђ.Y/x&|;ì_l.V7nnkG: hںgrZu_wg6> &4~* GGB2ouOFPۇxY4Hާmȶ,^Ev{]Je\siU5Jɛ|=#G)Ooe҃@HR:%(B)q A)h :J86qlHr5bGUB@ P ʑɄ 5zPYaO/1+p&+0AAA! Ak tIB=7H%Г\>tWgbL]L)|`W@CYoƄaqΟc^߭]7쵚3x=wԯ endstream endobj 78 0 obj <> stream x]Pj0+R-.c(A;iQ֮ ,dCV0avռETg:"YBr'f>؈|w~jTc71+@`/A7L295%H=9M R m17Q9HIUElMǵ0I<*?_U] ܳӴx*V+Z%(nI+m*uS endstream endobj 79 0 obj <> stream x]j0}\vRہN[xtBo_Ϝ|"+D7Y5%M"9; /ڥjzbQ毟 ^]ޮ_#1pibJ,ls;eM-R)q|PObf$Z:>4'YVO\=EyvoO%p'U:JrA@HirSP5_ɃC?g{^oo=!8C8}8]!/94M8Cd= a  C#C:@Q!g\Dz ]L-{}^׋_o^"=z0 8.$h endstream endobj 81 0 obj <> stream xmV{TWI!FםљZm`E*+ @H o!]:RrxOp1Xv\ UrBF{%\F=wiwnyC_t0bj4Gni%nJ<,$2GX0h觞HQӟsٍ):ؽ?:B !K( T7`M$0< h'LW>oN^MB/ &- t $> b104 @4Ҭ,p-ѯ&r+Ě}fxyv;%{w G;5lWurѝ18J8=h0\E0 `'N,'M [EfCfLÓT#@ pʫvX?Vp:zt@18ËpVeqx}~sv< ĭ*gCq2*{JviO(% Ǻ#p,Hk F[Tw8Hb7শMH6?xWF=?akwV&U,ӷco=Ʃ%Y2b+I Ewa6UNǖZC8Pνef%85 o&JV8!_b|D r9sh\<\cZQ endstream endobj 83 0 obj <> stream xk```T``hqcXsR endstream endobj 85 0 obj <> stream xmVkPWfqtLkwFE1ZAA( u0P Py kQ mY5Zr5˪뚽Ml[?uT₈D"yPp( UTh7G )('n3:I]>;u?y"ʛ5ʤ ߤ M"NqCf#syoD$/І&JQ6h i+||V3Ii:m@Ylz_CyQ[TITt ŋ BRRkT KwQFZt1Iċ hS©S'Uzu ec:ʠQS;©6PAh5WSW)􁷷cw›$;πm!Ao eH5PQTQs %aFV k?$َC#Z$a4g7ps|0.qb$=?|2ʦ yw*]ywϡPh@pc&2Uɤ3EWO)3־NTZlAJٽb^Y9w2-ThWs AQqs}ڂ"W-@^_4:sMہiMbҞu)lJ=OP:EA/IYy14z :QRn> q>Xbov.ԥs/\x>pRޚje|zaṍ)b='Be eRV^7L\`QBqRH1)sAano2\[w %; $}3 րjgZ#GIT4+~G&DrX޷q#Xn|FX 2rQo-9+,ub3)5(UoT2K0*qxj+y" Tʦ.]$%GyLeMBp gR7vưRZ\\VRl魖JU#KIYt&w}m} endstream endobj 87 0 obj <> stream xk````TP`q`l(dpX endstream endobj 89 0 obj <> stream xڅV TWD243CW .tGk $@ "D 7fֶzGkuvGK[i-?rsv"n_۳{̝{~B&#$ɰĤ?'E2}M PaR1CD0$Tҋ*@*ƅGh>,Tc| p#$J $s3|R}{|?Kg!?ӊ4Z,W@t|=!#,#oAme/8 !5fMLݑu͝Od+8a>Vb~caR(k/ pW Ex" 4Á00M `5Cj+$T>pG\LOVp!*Hp%\z]߶r .ɭx6``踟[; Ӆe{zdj-hѮz埇~PȾ :Zl(ݺHpAgq&`xaT LdSƓHG"G6@/=GVkK൧oC8VQp8O&{x6zݠiQK:/RaSkōLC+^;ji|d߉z܊n`Wn)T65x<Ž ]mifŢĆ^#f_(9ԁ39'AYxF(eHRO0K 0\.\&?%t#0c EG"GFV*W꾁;a7-Wo?`)zR}C&.V'ז̜,C{$yo)9R OWPsóמT>Afn*DxnJ܎'A<+d^g0eγxe#~JYdk-; hUx#fv \/$,ըT yUe;;kꮫToֿᨭrP =>7g endstream endobj 91 0 obj <> stream xk```RfH`x` endstream endobj 93 0 obj <> stream xڍY XڞAET:cgbk[W]Vź*UjEYda_$l8 ]VO. } {<9'='7lVY) hcԉ(^tF :xDGH F9c8"9ucgח1'LFcc/`w ?UօbՑsW*⒕jlO9ER/A!ъDUt4(6Ttt"NVJeA0"L+*ըdJ4\ĩxH}#"UD2ZU ,T )tt"V-]"Uɤ3fH*LVǽ6sZP רfʇf:7cF׭\g:I- S(2uP\'>بP1Eal*6 fal![-V`^jl {{ [m6a1_ۂmŶcXE`X`ÔK,K<.CcAq(huZC%Yάh pRvzxG8:zgґGstvThͣo>>mXbl\2; ܎DA86N/j2H/RT.=A!?$o I3?[!n6j.1[C9hȬd帵 Kpi^q-U]!?\miIF]tw趪ԢbRZjw棥m30|aa}H;$}bߡ}ki+$90UK->.!WF|7ةޛpQ!Ɲ.s6oMhIj\)xir* hV$ǯ |o;+{}JރfYfIdQ~3KŤk Z`.Ow*nv`` Ⱥ}8|#dGC/+ fv?;}^SCp0 ?-%%(C tY&#]Fu<߃=+uL**`g`^ z=̡um=6BhQfp=@ƍ{[ku{nwrsII0d:#ffe~8.{bESxld5-Muջ?\:3ah4{2tdt-.O~pM̒Kʊ’RڲD1E3dnuQ˞f)>ם׮qX|UNQ' "㝱M~$AĽP|6;'Cs20jSh{{NM 3i1!,[lTGoN)Mdҭ)_Q[,\ ];fڅRX*QR텖ShXo;QiSY0Wn):LChELc+F4p^(jц3xQ:sBSl0qLr,yF]1 dQBvw#mb 'TCPXˬ%(K ~eCRPƭ"oabyBn_×D;]2ߡ?'&֎k n?<|AV4z,SlwévDW1n`9 ~Ul@Q-oH#|:Ԃ<@U00c|A1A9`t>9CI[+2hZӽi76|{5Cmbh~BԈp.6 rP0`y:;ĵᄅ(grzHb,ʠx2ȩ(X Ү_Rw$3UamdVѡRbrj*ګ퍍) QSypuGH8@ pك U 4d69=V%p-MTg&肃hiۃcl2{Ael"B __H?nٲ lD'md>h=Z˗vv)=5dJyfUImQ@$3sԊj.VigKڲ↖}7U5FH6OuJ߂`F'e(L|µ561Xy']UZʘ1qY<~;"+tvtZp_?:}bf?FoAﳂRX#[rl K |qx:Ċ K ] nuK#n.bkucv_>BCC[N_pB8>إT='hr$ꦘHg5SbS>TP=[i FdZΗZ.,n *|ŜZS6LLɥD&8~XS_:%:'LJ'91dlqFLb"71N;w}>_֦PHLx'*~Y11 ХmĉO)Vֈʇȑ-> :ԉN\k݅|MyZi lY_  DZ*u[Jҍ,Z*\)ΣȖmj -} <Or; 9|2 nfN3utedP|$GCK ]>8 &e)7+JwR^o8%GOB u)w`C-jK%,0+/ ,CJTm2'Quydd fS9P7 ̦|7}̦"2QgD=B<'#5-9Wւ;4dK,zvsbӢ'دQ>󣖏. 1ܤ8v8.XzF+sNO{e([R+[gk=l_y'sJ3+Ӹ)>iP\L9*c.c5e"PMZ3MX\ӑquAtsgFݫpoXc !ދgy1Yi.9뀆BkAOH? ų@nHblcZQӔc&Aɮԝ-bo[⻁F(Jp1,zao(8GUn_@=p_D5`͠R >~+=?MSx0%gۅ(1SHhdAg譁YD 3N.|5KXA-U00m(PNU[ * ⾮jn^Z/MV~!yXlq4ْcI0eОhd^d) A/%(&vHZW1)Е%CyOuL1[, ^=BZo,H%S2tKKUgr^A J7[W&}$F7mۘ7|Kkn~ptebƚ[^IZ/|LX[#L4ab&nѷl5_~EshYSU22W M/1M]H-i@WXE\S$/UMIUDMFc{Μi2Ŧ*ʁ'm'Py_D|JcQUD[\?gOR'?vurad9X}9f'dF{|wOpDgT{_Z,[/G6#K(Igd<+֧ \AAh?U&p .#l3άlΦ  ;D8"$iߵ`3֭5#2 eo{wg6dTSFmCCs+lIK;AuۨhV$`5<{B)I1[HjRkh EVMƎc1@#xd_*ͳ?~xHRӳt:W"l+t[.\{7Q]u7 nIΫ>묫(),K]~9G7?i7OB90 gƭ61=W: `_a3[Y, kj滎_K endstream endobj 95 0 obj <> stream xk``< *a * endstream endobj 97 0 obj <> stream x}V TW@Q#CgWQT Zܶ>V*aDHx QJQ^"J=V=Uڮ]9v j[s:s}B&#$ɘȨXFi6pyC*\EJF`'|d_)= |ᾲg B;]vQ(rr*Az“M $J57,O5ւ9AZmt>7]-")w]m <1|:!UMĨ BD(_Nb[ u>x #$R4(mףbjJ1/<% PK OݰQm*,gf'OjVwך@|twh _&6(sчY`GYfQi\qc=o 1@*@ A+ Z)l<}d19TB"a^ |RaxQiaYfAcr6 gM{PQ5ٮUa4 a0\(*;z={mO0:XxA17(;YU`/ǧL10pWAá%r9PRa:m} $HQGcB6H0(_|0n{ SNM$ > $;#JUtwdfyL@yKX+miTR$s ُ*'qߞYj7?ɢX<avM'4OR웲QMqtP.18@.$TSP+0zjluUn?'g(ZGV娤::Df CES98"ZDj:ߢ |J0'wS\18%*b&KO96F$A#HPJ?&]&)4!Ef@MUnt4-EU$op 1Mbܳ^Yֲp#P# U#I A<~A;OcoO[0> La q$z9vllb3P|ZDO@U !PṏiQ8!</E L1A? .slΓqO*x"u1zX628ΒB}ZavqGiv`7{5n'oqݗ ˵%ťn3 ا&XlR^^)c=VWψ93?kT1b0!Pm5]dwWMYie@NRW8w_@.%䧇@uA&ֶqSyH>Ʌ{pI2StMAO=*O8{s b,PY#݌*],2o/f^['?QHYKfNhOjav嶸6zr(+IxV02 d>Q_SQQU͜:%_{ /|Pw-Kۼ/Ce*_x8u_^M&5s}iUf.:x_lq8JycP~*/˸;ވҰ1vۮo]fQ0˺W/-E[&hd(:ywP}w#(t<> stream xk``PP`T h) endstream endobj 101 0 obj <> stream xڕX{X׶2AZBkp*zV-XB!Ixgx~m뱽Vzvwޙw$dϬZ~a۶)䊭}WȢk'@8hK8NI'OOU[aBĢK`/wz8挹a${>[fC"B%kb5޾~(ZU4F#Z“=-"W$cEbyhhh"+rWE,ZKZ$U)joQpLZPŋwD&%QDyD%HDBv6*H\-yyDjD(h 'Qf>6~=kkwzkR4hJ%шcej !aKױ7e[;ya>rl{[cac-؇6cl'`aX8&",KK2ee26vXhN_o:WÀu\os;H8?+mŇ۷>g;wM珒9P9Ņ甂^|~("B~g#Cf'~KDž zt\B_~NbnOgtֲ~1)+d̄ ODKv *̸ hN|wlh7SXAAQH4.+)}BcB*e}/NV DJZص߅K f`>>jl9.ˬ+ia~1%Op ^nc %h5|iY {Ͽqk|E\`Ơ.GM̎ p1˹Ix(4L#V -4:+>pRZR\Y32Z ՙȤML$SNg!0 oEh'PD1Zo[]QtD7HuL";p>0(oב{uX81ƼĜ?\ygZ [ #imRb7ɎN8 xDI7[#%90RDacKpY,.}VUo4Ykŵ˜v&QݸGX S]D**,667ScuiRn8Ie$"E@CC-:ECSIļ'N` DEOr_N C_/ 6|wRrr7?|#'sxx^P]UM36M2*X.zļxYىBeSF`:3UU4 Y5*޵tE;rw Ф\'ܟa_aI5EMcC8_@G@']3vxu'[fjf'Ñ .!pqA9s_Z/#6Aݟ 7Cx-]!RǫTUwwKK7`Ŷȥf:Eb4xV$ EW~AOri<l`ܶxB(IO+xAm^iA0H&|A@meYi"%N 'LT-H˺9ʅC\y$߲B)AYW'ޜ$raГ|y oԷD2NkvĂJݯJnܘ<ìpd@ulV6٘,M2ʕfP8vNI`59ζ>?Pxz}+$m`y9+d#G.5]ƾ>FQS'T5Ok2֔u {^YbZh/9h1#^ f+nvW;OUz(Ar`n0.A@lq~Lzz,/`5ݡW"}[g"k.)PH,Ӥƒ*!HjR<> J?5rtZgcD6զ+ NVDZ#{֕qrZd41rc7O C endstream endobj 103 0 obj <> stream xk``0?d`KXc\%X endstream endobj 105 0 obj <> stream x]R[Ha]#uZFg&^z)+aԓ;:zKeMu׼h%IK"Mfl!9;Áj50,>dK :#oqSʰFʒ8!@$'SIjK׸!a|6سd%s&nSxN $dpd wwlm)4.36r8yXj0\䫰UF¹(hm5$41- iq!cfz9r2^WHB` ú\YF٠!Nיa%CjD4N!t^{$:vyӻ-qzNWydU+Lu$C;B-#2ʀU%K$CTXjЏjb}R7[i@OWiGAw{k3;sR{v|hpRf:*VDXX92s>5銽 ZibQHs3cChr.x«B}{+`"-l$!p jk}c,ZևΗO5^\F6Z@;mӮX,j6bR&/; P\ 'Dx$JLp|yBƂIi ?% d endstream endobj 107 0 obj <> stream xk`M endstream endobj 109 0 obj <> stream xڅX \WDP =j{ׂkŪyH$yR$A@[GkՠnmW+nncw@vd2sw ps#x<̰pU E޹eX +;+<]mnQ? Wm?^=pf>9qs!t!xMK<&0ϕL 8i6Ix*-G$k%^_Ƨ4)II$_Q&I^TJXED(ndhjDVei_)OHT wVKX4A UKrdHI*U+ Kj??D#JZm 2Z0Yp,ic_؆k7Ffk%*$AMRh5*2VAp/b>#bb)XIN"D8xx "b'Kr"Pкr9{݈r/KK\KI2L&>/]nxvL_]w~h+{Y# lT6'Uüآ 9Ru `hSl?=Xb0J#N*Ha9]u>A 9jʕE:<{h^-ùn[ \<!oH(TW7 "ujP&z2 f ~QL3 ֆ]) .]<U2+:cy(6.+ }bDYq< u64t lA'6t8ϬQҌ'NeʬR_#^Nw#Ga1#7;?h .ž_C>9> <wEO:Eք1ZeG}~z7 ߡ+(U5n_rtB,/*m '8IkIcނ0P5xƃ?PWO76ʠ%[:xGQ^[n\ /zͲPilDrJ*K֌*:(=Β687i ^ 5Dh` #m҇7s# qe}DS'>A?ƃp/ $" kܓ'i(پ!_{veQ'gN98Oj!r1Q(qP% <AK8sj04/seeeٶmh-b gggm.V.1ʁo}Pҷl͚tǙ*x  xO f*a- JRY2-nYgZreeaH?3\My̸jq@+Vb |ef.3%"ȸr}z1GȊSR"N-BzaG2O գ0~0dBC);a3i; h|*ߣm֕ KꍥFɴǴ煆tnn{#4>˗f^%h T'-f&7s%2kkkjL}"4uQؽDklFzR\Fc^{wOچ#Ѷ) m>cK'DLFn*Ow9av`k==յ m9o} endstream endobj 111 0 obj <> stream xk``Uz1 bl endstream endobj 17 0 obj <
> stream xZkoFbPk ~A?ƈd,D;ʒ!y=wHI#YZI6ǽ{ΐCag3!ILkf&&shWy,Ha[tLR20\^ESb 1LP@n!J 9VHqoOkN3 y[!AeuW4ef`2 I3(!3w>TÇwQn2\f瀞U2d<>ǧψh:H2C00m@dZ,`}˴ k>;+* e}ax*X/"Pr:_U<>_w]a]^(J.ub*I9φ7jVOUoZufp2G&:MҬƽ[@%wݩiԬZT^K{`X v Z(V;XN1 hLGU׌VVktGmzSaZh]3vVkB`VXD_*t+d,NbcѨ]@no:s[ڑa3ĩڭ2]WֺLZ W jfkx涘noiIjsd| 4«/q q9f͋9)fa5*\WWsU3O4$?Ne޴W1"?#fiT)86<%n-~طز.n:PMP(ֵH_Hث:յ\Rm(L/QXp-QM{fqcuIOBkXf[rZ-Jhٗ[ZX2;9S[ZD jt&a%<0ֺNe2T2<<'ϩs[|vT̼]n4Ln[.*iwrq~x;~ݓ\jө{Uw  8Sm9>ll^_ ntdPg勻j4# )Qy킔yXczUo43}M*S!Bsu+=) GA@1UWyӥ7pРuԀ4(5LZ[H'""TS8)"#Ċ)ڍvZVW[IZٮх)ȶtlZg< i5Vu.!uPF6jĒcL49*=2׆B:1QAUԏ3E'` 9PEy! 5@Q{$"F-H;2 L<Cvc_ i}R6z"85p1w<>3]xG+MJ&j*m("pp"f<фqSh5*;X6MPcqRE2tMn~(~5^C_"P56`&bբ|=«0v_C fO :,EL&bjRkslS{O3]Hhe;bA31S{2ߟ_Lnzc/QoJ/?ͪt|5 pVM? &{,SnoG -6 \-V6f%C_#,0sO<7tHE0%0]rxspPDuQ!-X}}+YOd<400e5f2b0032e15e3c51a88f6ecbc885>]/Root 1 0 R/Info 2 0 R/Size 113/W[1 2 2]/Filter/FlateDecode/Length 282>> stream x%йNBQ3\AAw׫( vZkgL,,퍅`au='̌1|&d0k؅m8=8C( C 4p*i#d&Afhv 8Nn@ Db } `a `F )48L$_?K,%f=[_z$^$)֢Yqrڗ|F+/EOHJZKR*J]kYZgRZZ@BjWZRK>,d endstream endobj startxref 34021 %%EOF rvirding-luerl-772b25e/doc_legacy/pdf/luerl_old.pdf0000664000232200023220000011634715114130670022716 0ustar debalancedebalance%PDF-1.5 % 23 0 obj <> stream xڕYn,_#`^ ]wA ly&HFmF(G̦ˏْ-z|۟y~LR?7݌*}{qZLJΖOh 3zʦfUΎjC딋2r[w`،WvhTƩh2YWaurb/gsw0Y#9Sllo͚1.L{.gӵȳ>]^":7w yʛӯM DxSWxW_zƛ w:@>&m36y h`o%lB<OEzrb/=yGiw\YkmfޒY2Y@$~5if :xy5-TQZ-YG\V+|d^9Ơlr]^= np`hrQCFTdhtR5zЉK@0j8"= Hhڹ8Vs5Koܙ|hQW D0&)* l+ʴ )3 uP4O%bmcF[T|={)*\SwNl"w {xYNO2#âz%^HkNlSy/xy$š%\vWK )bAM48yVt*~mt8(Tk-6ݧ0D9e;a 8 ܵgt_V) jug6+v\})B˵z>YZ£u<]tKT=}M+mlHjwitz/zOeJJD{G>WWŏ˽Kgubt\S|Wң!׆+9^>H-R6)UQK@Ĉǫ4vq^t `UDcL0dT[luB$ 8J!\(NE빈~\^p k^YoV4E?Mob!+e$rmitS?^+?{9Lߢ*NRv^/uZ endstream endobj 28 0 obj <> stream xYn6 )`1ȭ!gCRQe#=dCcIdMNjOMҿ^IOOӗovRJDkazNN tr~4UJQ_7Kit=Hi981Sсky%S3쐯zI#' R7HH@KN.N'U]g@liB4xԑ+|#ٗo{;c,(4; 3):6͢5d.]Y\de3]XI.sՖJ(u+W!Ue.z&ShX1w_.lad[nYEsNJSl[11/^֫MbLW 1FwNe g}Z$Psj[Fawr#@ϮπByUs~]V)sVNy$P ԘK]R>q\rބG $?rAMYYW氕VN @j]{0^䕅jS^~#6 =[ܓ5D2,Rp۪1xfn<7q`=`6~gT>M>ޭcj!H~\xI:wnj!ptI`{5qXЍ p-H K6DzFZ?jaO\j툙2ض[]$0@Wc75Պh=cJbaq_65DtUݨv~UWLݖ<$6JYwyfqg8M"*mX~Cmsl~ K6^y}&< >[O*`~(ݽ=zbdؤ9`*M(zf CF|e&;lD]}Cfwf,H}d Um^9k F?ߊ~g}v Tp.ޛ is<[|n%֕䛵lS=s}wL͐'/- HfOW)X(;\R#:ƓC炯 t}~r=ϭmYߴHc^:jQ:'dУ\x}T)|>#8ϣ%et۷Ut,O P "a)kpw~ VBi=/i' endstream endobj 31 0 obj <> stream xZKo6W%g z+[уoz!CQ:MQp?3;M.׻׿We/Jm ><з۽xFIk| ֆ~BcG+'Ϲqt:"O4Fp?9p4#i,aڧ'a}Mo]K:lN&e؄>Y o~6ת™^Mɟ!f̸ y5yɋMi][.RAVFYdiZ S[j'`s\ғ >zbĉUITYqh$.sٞ' v<.: qt'SʛQ5&Ȝt Fd*4D* bdUa6פ Q9I8?34ZD`f=#UhF{'!Vc]Gbl̏nZljqֶnkw:gQo^٠֎㽫L܋*BsJ܇:Z9 8m&ǯI}"hc0.1ݙlrFn&~߲Sߺ N&v.{e{7~? z&sCqj}WR!ꉬSﻞF4sHθoIҙY?DCP+Ba? # endstream endobj 34 0 obj <> stream xV0y @=cm)JHtH! a(xN[-4.c7WRV*u_E`J:Y(^]/1.C16 W^综-L>^ց=L.r:,dK$emA`8}|R/z8BdӞy>vgYP؄'(Jm>u4| }E/ƕ`:zi4#gYsuN)¡ N{^/$\]4jָRX\KE-n,%\]ڌ*K;ޑѻ2Mv?4K1- mwb1mʚ57HK endstream endobj 102 0 obj <> stream x]_k0w?=vVׁ"ۨt3҇ xe!78B+8r+vRqB!? ,|!{ r }z܇Z<49W|qk_VťlJ3sQPZ`GTAͣGҮ-eօ'EٺpZ%k!^iLHW!ew,zKH[Պ=CJGUlgd|y(R݌6.$% endstream endobj 103 0 obj <> stream x]݊0}\vYڂVݥ 5JԋjN̙d$OsN}YVT~%50lUm9iRa&g>\xG<ݏ|;.lg{f~r2;`P^1Gsq.uϢ`\q/9T>ֽoYlE6P;< OHL2X(+^!#([Vl;>u*EnY tP习T2(=EApqw=H+P g!C Ҟ{<h3⡲ȷA..G򅚠 mCTjW+/D [K]! zP9K<35(뛶ޓ8?̏ endstream endobj 104 0 obj <> stream x]M09HZSRVb2u 6 i=H;_I\5uV&JZ W*)S Dqu ih\\{ʈMO$ s`hTIQDħ98'yH}wwe%^ۥjM0$QYpkZh "WI5];˭3l$-i)<-SVH 5QH/PP#alT(lyO,AJrU3̗Z#f yǰ]/qnܯisu3y endstream endobj 105 0 obj <> stream x]Kk0FZ %~@0$q αP?PE>RCǖ=Wbw]3WŹjgUV(uS$_,vlX?_}[vQrW^Hڞ1>XYq=t^ׁLMݟkxzMq/na>\u(+bu8=Xӓ5gv2J=Y<' endstream endobj 106 0 obj <> stream x]Ok@~=5 4MɡG㎩WY!߾oɡovf NV}]ĚV EcP5+Zim=ߺNUucSUuV.Vsr&A9 Y' ,-\yԓmEV%-I;u$'XI˹VơIUFVO|~{k]RKd!x-!߂r|dhP;f( @^=( h9ȅf4h>CxJNpT E3D'kjEKdjrmD-ڳ> stream x]Qk0yCI\mAhncVo;aM$Ӝ&𙛜sqY* "~ m=]7$tlJvsGqL"Z,?vsm4y\}﷞dVeseNxe7qh݁w/%ٓ(?t&;$*p\ sk ڞ('Sf|lo]ñT.$Qi d@RbM21@y 2%(@O4@B/ $YoJ(Kqa3e@pfX=:5^A VA0KdPΧ:++x1 u!a ظ!BWS"O >%!F s&kt@=g⾣G}ϙ(G`BX`7T2z󭛅wzӬjs~0a(,|iWA endstream endobj 108 0 obj <> stream x]Pj0+R).c(6CG@M֎ ,dCV0av),D_F-Br Cp$)pFIT#BT}F*xXzlI|Y4Bj^A'kؽsᓟ}Fvײ v#J 1)`8iP9 cW䵫UFg[QCn,,fc\p$$>Vu; endstream endobj 109 0 obj <> stream x]Qo0}tY PhbHbsa0)Ma$|gV5=?M'ԳQ[w7؅Fy!ge#{G+B{~/GwnT/Lϧ< ŧ&o7ǭv-cz`U]e\;Lj69gGrk}T/M\t+ IP5y`xṘ'HgmJf,CyDaj)$@> stream xeT{LSgR:꽟o7E3681B PnԾn[jqtW2pTD-l3cnFݦYwdmd}}_98&b8WZTs&%.'Y)ɓO<B JOI F'cӭ@:5zwHǀtK&b3\Kgѹzl32r:`͉L+t%Ltf=X F01mAg*L)J[ث`R6:+v `)clAZVjFcK4jc՘6jb~iKWkV,/X@)czڦ3QX*u&,*,[ X1VUbfAH0I>! ;l\2>\}!Uw`yN(~LLdo|M- ,T͘&i~ Rb+l5$4*X7&BGuqy;SW#޲+ sG|+`q$`&&O}]OYqZ۪֮*_T"44^E"8=t9<$Q2`QIǽDjFė/v9j=XO+%GyaH6<gubYl&*vg|gnL㡰LjKT%F$->/>> F"MX4Iвo endstream endobj 113 0 obj <> stream xk`F75  endstream endobj 115 0 obj <> stream xڥW{TWi1];cgb*V@ԶX"- !<XG \A^HLVPEkHX[iv[ҮmݽC/{v{9Μ;w}wdT65N$dqBiT馢ky#hc܇/E~S~Ϣy"H LCRGٳ `l 6{% ~YDmb}BcH6d/ צe钕*<$8xS yX|C\B֤OIiQh&h5x*N$&c/ɍzN/W4@y*Y/7iu)rSqzEܨITBuK|VcG&'(4z|b\PUCڳAA2PS%cA郦-^):vqh4ȓ:y?85ƷycX B 9,[a:, ی`{}X<`)p} A`g[Hh%Ecc3D3̸0uѿVP,u=?2D4re[}^a8>zН={[7h\- -' sSIЧW_.{E]eW)ڏ°vws᪚n;-]`iarg GB(P-⯑0v..q1f"3U="Jj~Uqq(cBKd,qz%6%S[U0ܠ8 tw)vAr; p6UrؚOHBrS;(jAi#-8,6KccڈBkJP0\>tG3q omK=Lcz譫>(KK{ܘinVHZVG͛ 1s!!;]ZY(HTxv*aUjzގAұ=/W15-DK.y"8KVn-+ȳY6u8* |qA/N 4فֻh6/}=~pVL+Cb3g9\H39Q <[Ndj.>+6O~+h‚=twwHl_Fο[(s>Eݱ9FQu2myĕkM,tնhKc1ar&M4eh:Ks^[;#09>rjoMOݖJXMVMZ ݪVPhr}Ns#E&ܧ۹t7҅b; l2kYZ()q7Ў^W/u|4P\9OC&FMp^qބ0ᚸp]p> TFqb" ':Hpץk EcnE•2@^eIn~ HP |ϟx _:JmuL6w\C#ԧg= ڃ2DzN=W𣆅!3jϚ+'?E! һNo9: أ \Kp cd"'NWMuNk" F&-h; "V[A AJnNz~ΥzPClZKՖ6fUpÒa&QX;C,.~ÛvPhIb(úС=Wgۃg.L/5u {OfdJL}ޯzǫ .H XAJ<r{ng:p陾Zo;sdγ[^k/wee~Cs&~/n  endstream endobj 117 0 obj <> stream xk``P5В  endstream endobj 119 0 obj <> stream xڅV TWD243CW .tGk $@ "D 7fֶzGkuvGK[i-?rsv"n_۳{̝{~B&#$ɰĤ?'E2}M PaR1CD0$Tҋ*@*ƅGh>,Tc| p#$J $s3|R}{|?Kg!?ӊ4Z,W@t|=!#,#oAme/8 !5fMLݑu͝Od+8a>Vb~caR(k/ pW Ex" 4Á00M `5Cj+$T>pG\LOVp!*Hp%\z]߶r .ɭx6``踟[; Ӆe{zdj-hѮz埇~PȾ :Zl(ݺHpAgq&`xaT LdSƓHG"G6@/=GVkK൧oC8VQp8O&{x6zݠiQK:/RaSkōLC+^;ji|d߉z܊n`Wn)T65x<Ž ]mifŢĆ^#f_(9ԁ39'AYxF(eHRO0K 0\.\&?%t#0c EG"GFV*W꾁;a7-Wo?`)zR}C&.V'ז̜,C{$yo)9R OWPsóמT>Afn*DxnJ܎'A<+d^g0eγxe#~JYdk-; hUx#fv \/$,ըT yUe;;kꮫToֿᨭrP =>7g endstream endobj 121 0 obj <> stream xk```RfH`x` endstream endobj 123 0 obj <> stream xڝY XڞAET:3ժhZwa {"a $9I5dVVm{jk/]KkKý? Jo$Osrw{g=&o>{֌@'zu57i8FI B5{4Qhi"x dQp|O*th0 `ðثxOXpE"aˬYVFD&D)sf͚;X"], snpnK'GK}eA>iD]]EH#1S\A1ҸP)7Z&pYT$.]Ȥ3fH124H|kL2%":pf?'f|`RLf޴}+"^! >?vƈ09ƿ(l:6MŦaӱ 6a "l16 [\{l>ۀmƶ`[17a۱]`Xc!X(&°p,Ģ1bX"$EO$[i.{,N FEvvW"aTD DOg~!CN >4`0ae.o~iSӅG9fdH0QΤ'Dqh a41|ʺW~}c±$ʝ~߸'-N4,;N`&ZT [DLm^"Q'e2ȯ y׈X|Gި/#hHePgan ƶ F R=-r4*^{| ]L$sqsnh*$p =nNw(E-^6zo}ceu}qfn=p:~o׼Y<8/P JϑΣU4s\%>)!Wzl=8vNLE vY{GJ(@b#$^홐H8;xG"EY)8엇P,KXYE kajx:QۢduLq/Yd|:Sv a>2J:>>a\~>4OÝ,J2{EZf$I!KVuFK#m?OSwT$* OR+DmOL&4E.@F!dmVaf{\8LR 4i,v _@m{· v. le|5d22b':Af" DMl4>>!t$M1u11u1MMuuM4mk-}$^z՘.Ȩȶ"ڰ$bZe9Y9w@v"`1L~,|{%p+l׉oTLUC9;1\i8nfwґq[L&=y- wo1?\u9`RҖi* ʻU2n w~ pz }m d(z@Yˡ>hU{icW7'` ե9E.5ٚ-g}{Xh/&NgB^ fE $bpE)4v P1h||*=fmD g4#1#Ri0`5\<4a5$hzeM!3MNfTjvv Yl2G(0s"M:0V1kX J)_PO 8YN)8g!cIuDGYxu{7 _7o. ?zcmAu}4m [V?>Ԑi|+& Q ]8D,UI{F-m.6Ft ߣkxb(t2l,B[@1@oǎ}h,etJVBr4(kh*)Olv݃;L{>h5O<]Jz#9[-~]މA4'j-q㻒\MNC dj贸qk(۝Wɤ SAz" 4`ͦˋ|I2Ng ; )Ƈ Mn=}q/C,_3uETKn9$)(c7CԼ:T8#$8m"YN'o PX¾«AnbJgRHY7i+Nn4t |+A1pCDNz% X .Zb`őFy3Q#{DEC;[moFoV¯͡g&Sh#Z 硹pg__<(K:_@W#;&[/-6 ߣ*Wq dIEJׇ&Ǩ\V~$32BHy7 #W>rNp/'o&H7n6R@fDzd6#:4UׯjOleTR+*:ZJ!Vh$}tJ"6 qUU_I}T0ЮcՎ)V<}`F>󙁖# S|ǰ1;֍6C-kV%[AEN?;M+,PKαϸ]g\3OD:+o+BP 6&2:xbP1xxEFhom֨ay"4Jza އ.`6M r Y+ǰQͳVKxKl;g5%t[6ZG }_VaG6+8qIdt(jm0īOE}X0/"kn4k`(kzCഌA|O3d웗 +' 5zׄ'*pA*]*KJIϠ$8 n0OgE&t%{QԤ6&-.aݱ7TI|YE-~(ŠɚY_,Kۂo5[=D; 'w!O |}*ek߳V fFME\H~?= |I Pz{^;(o#Lr}Hi8ZI}xH]{-2Vv*_#?r ǰlMQ;]AAa.xEP9Y27[f`=:>O7=<74F}_ip)!cfO4hX;\0am,nƲIf1`/R=sڿb7EoO렎p+գ181dg"Xt_! eHFs(g t#(d^Q:=Dl#$\H.-B"F\6潰0LWFmE4iTJmUpy^ H,#3R['HvPivE_$EtMT_ruh9[WYd+oSᵩh `tuĠ^,}! MPIɡ^^3 q/mF4)sO x2ډDM x᜖W'HbHŽj샺ۗo?"WM}BZ჉vJ7mʅ8^ [@\d4q2z?un#\꘷d a TSB,33K_O}Ba(I5pw <¼G(\pb޿@ktq)QdD>Sa|. YVՏW\(yjfE\ o|mBj=PQ% mMoNAm5v͡;aiۑLQɔ{2g48Dv\,6zU]C.hd O|/(h`n9MW{)8䋇Ў2 U-Ǡ11b쇣%!>nEkJ.]]l֧:O63 %deie`dk܂#LsT$3Sĕ Oht٩<}i^_UE PYa] #X#v{EmK TIY_d HUAZQiva*z`c, 0GK⭲Rde-pUYLMP)H>SAkU PGD(=`m,Aie6m(jUVZots:/I3~ GJG+dkw4d,zZEuäS8~h)m7o^7X Y]{4$w~ps&-۴,VNx8SFuUNnbvkumХly`X%L XVPe% j~( (OjdFUXDR ȦLb p5תr4%T!41ץ M0㒄\7jOSP d6+KE=lK;@%o 4H?!WNDPk,J nxPo[eҲBhq "jMgJ'@LBysY:CyskDa˵ɅL(,/~>|u:e<\5ۚ'5 tFr[\>?QHmdy:~Yy-iu.+7`hR0+NNR3h1Ak1Q\Yc>l!WO1J|UT9?D[)`=Ǣ)kc 8/ֹ+Օ&Suz$*fgXyj z]} o*'q=݁'t?_GU2ѕrޝ/uI=2Btm,+nwˉ# (4B-> ~sgP;z7QJ$QA i&GP?}xcBfvj24tSݞC 46#pe_RaOfK ssiEf\Ni#{C? /d+{d}?'0l+JP{!q;0`]0`0^vt4ws F1'7qa6J endstream endobj 125 0 obj <> stream xk``_g@V  endstream endobj 127 0 obj <> stream x}W XAETdtfp .Q  EM]@ !"Ȏ,VGjJ>~Vj>3xy{ju˝]Μspt$$IyAu8amO( BΌ+8aTps ΎI@@uA!eo?=o=k;꜇!!P? !^}dF!L;'BIRMiXhZƫT8oO_m7EGq} bo7Ҡ´:H-҆p&ĭ1bכFyrtQ&.`ħQV1i#X}贜EADk&-q&Ĭ`\)1y}9e_;?pG>>1 1\Ehc4QkMx $1Fx>/G&7AZ yYzp,s³<9o;j`Hd[6`Ra]}<4F^;k|U> c?jK\lR~t0sE(P]Y M*4^Jv~_5ۡ&0)cTA s@sq+ܼQWGE+Q)]4v+BF~'3^SVXpXwLjZh4m8!x O+NVd7 _'?ze`8ٷjRo&i":bp`%SL^q:'ϖRl 81@Q]ISSQFF\[ xYٴbIWs 2b*E #&.) Ӌ/ a@FR7w"u,?f_s%$aG⫪7ګ^ -"\ N:e5-۞iKNQLRvTek jB4;sűl 1=76%qN6oX\w4<F2KPZ 0733;9~\&>hY#u}ĵ$_ )Zku]S8Efʶz,f՚J;]j@ ̞ ]:pEIl>f[({n$3{WItD\Q > *'w166/iŪ4Rܛ!* MI8& ͻ [3 Ebk>#C C><;mxwjSz+NCHha!T@դe#NkD|+k_;wk[sJ^'=׼$2!K0ӟF6W\G3SH yIpJ*i6èi2"N؆)帷K$twA{<[q Aݵ3-:3^R$CikGdLb(/wY![7ޜa;K*MeQ}BDvgm*Vf쟵z x"a?fqMª|9qrwyjjiήj޳+ߖ33 ~# endstream endobj 129 0 obj <> stream xk``PT`Ts& endstream endobj 131 0 obj <> stream xڕX |Sź?ijs"((A6mӤmIkӓtM}ߠl^ Dy +WE}wߜ>~/ҙ3gfG@-[jԚ-s^Ԩ" ܣc6cԋM гC>ïQ/| I߷&O;y 4q+̪HM|c\m6X ^ZJtAp {ŹMXI-#JΕnӘpotF- +e(&J"-MuzBIg͕(RF+ſ:J.# HNjPʥwӨ -r^.3G*R]6o!A1WS̋cT#{i[Ȝ-Wݶc\ 褑r,Z5 Glj''SLb1x"YbXA$^$ &b3Jl'^&^!v!Nb%N(h"PBK7=~-"Q!LOu{OҧZ8UDvM??'pv։w7X@ ?УM=_S$tq*⦚\n"pAPm'ȏ '+DV |afR2Ӓ$ּj#Yd*L !AV-*YKnDխ] .,dֹIW!J9{jERP Lh'4Uv[{AEwI(뚏uVjxb h-jV8B nt~uF msfBqGk;]ߓ]G$@Z21UDUsXf!j6JV20 Pkt!Ro_$?cjP8Ty| bW|޴!F7\\nR7L"TI?H"R1.ugkH*xtdRol!Gl׏Sn޶iApI'߿@Kƻj{sD?^h0Z0Z߽Ѵqd2lř\^7r{spOmKjm`~9?iPz'{o<̘ ,]􀽾mQ32A[^foCLs !Mh3Ŀp o=`{x vLECRܩg -$L/p;pC #Mts}UCٴ}h6鐂OtR l19/@LRUFGt6uv@L3Y m (e|$JKM;`KU;@!Lul[S⏟ͷ,]w>/2ٷZ@JakrvRqvn8.̂0iQ)1Yi2^zź_ /p|Oy1r:ɳ͘sZc*qSj2⦈L]ۭXSF?EGbDUj(I߭^mJtʯnzjH1ǙdU6I`~1t߹llJnd; }K&G'B!P;yW~Q5[ =mcFgGJ ˒جِM8~8u?´.VVeHiss+߸K\iPB5_z@zeqpI!Q¥ ]'Nv'򀸍?g|G,tL`Yϒ~30Crۏ! #YY,?>m0C#t :*A󂓹7.qP 5*{" ϊ#yXؠD!-=^8e)c1p"/p XKFG7bmEX7R|D-}<144k}ؾJ'HFی))?,kOO>=,TI* O|E7s hS_eh¸ gK0rGa*L^]K& NV3ϠKؔ'!T32Q)'Ɠy&S.H5r HeIe s 87׺߽tKNsUx#"fQM[cԠ%Q{DEC}Qe[hL1U9Xf<7/I' |ND9[U Țsb%8 |@a#îu Td?b\yW" î.{x*I!NM,7rX8e,I\0 F:l U^8pɕQ;"xSX(;><M(ݰ++MxЄmx4mpk}#<_ v- V&˷%R2>r^/td)g﷭.ZwܰG//詠(Dz=bW֮_?3/=9"D,/e\.Yע4dd{yfV*UM]. ݓx%.s x:3r߭wN;sp .f+7oⵈ A"߲n*]'CEYC*] ױe >jO +{U<ғ$?㈁Ce(/nvrظά"kMr>'ENIiNCɰH.A/VyH=QPܟ ypoo+^xWOt򌃹dU@ "mZp}7[^_ {Ou^ C[+X{WVqƖf=oɳCLz*/3aL#\K<#G[wDylZ*(PX7bA5>I?ġW-pYA\QKtE? p~ڴ !-%b>/Ko͸Cx [Oeo} @YmSwKWWzsjXj9/w.w>,/S8~ܯE 3%>D)Hb٢'gVؘyވ]ua ֜VVlM$n$ڒ% 34p7=K s]v.NASb 0NBI1 L=#{]U׬L>G++j.rD+Kv^2*49-V:ʁ&.n<^ q80NO"Ѩ,L4$='=g> &٫pV/d&ݓa7N00F'AA#~A<(DrEg ^MׇAuLJ=]P*+qi#VYf+D эrș?->}q5[x h!/!ʍK6,)ϳXgN'L^>[.;%)Oa 5'9v>P;8V؃PigȱB B]AXT_$Bׄ:[]h9@/xMXOiT]+\T0W=!qI&h lKkv /K4->#q {=1}H&O]ۆ08} ^fBgc!"lC!^vܦzc&t $;VZk#r`텥G$XVao> )+py'?1 OqK2L '&'+󯢪ƒbܪ.?NM? endstream endobj 133 0 obj <> stream xk``0llϿ. endstream endobj 135 0 obj <> stream x]R[Ha]#uZFg&^z)+aԓ;:zKeMu׼h%IK"Mfl!9;Áj50,>dK :#oqSʰFʒ8!@$'SIjK׸!a|6سd%s&nSxN $dpd wwlm)4.36r8yXj0\䫰UF¹(hm5$41- iq!cfz9r2^WHB` ú\YF٠!Nיa%CjD4N!t^{$:vyӻ-qzNWydU+Lu$C;B-#2ʀU%K$CTXjЏjb}R7[i@OWiGAw{k3;sR{v|hpRf:*VDXX92s>5銽 ZibQHs3cChr.x«B}{+`"-l$!p jk}c,ZևΗO5^\F6Z@;mӮX,j6bR&/; P\ 'Dx$JLp|yBƂIi ?% d endstream endobj 137 0 obj <> stream xk`M endstream endobj 139 0 obj <> stream xڅX \SW!Qy4jJ8X,3YlʗGͧPP˨T VSkU* "H*bxJCi$jXpL9J8QL.7îS$ny7^"])vӻ7q\:=/{\l=>K^*z1_+߳7L[%Lf5h<n ),͒kpnĽ}Xu(JTXtSQF|W=Wg`t0i|cW3s+WDfD/jڌnˠ՘ͭeP-ΪWrdU1i zoC}'n?Uzn}Ŀ xwl:b3Tq>s/afWpF%ill70;q"HsLWmyL-Xj+>֋٬67tOWQ oKU8SeDM0P,u,܍,jpS`>(WoEz3q)+9ˤĆԵ%"aurKQvbkq M(Z$'AD"3KSچ3W3҃zY2wya$0K 챇#w4 jsY5 j.r-Nߓ*ϴf4Qh/~,"x:v+^fF ќr2S8l_O7b֩N7ڜgd@%/\ֽ+\:FC"q V3_zTvKڂTDZ!HcqnoAw<3 2"i(Di8nkEץ#HљXVwؤy͸쯶s.d7ǂH֝ Ɣ<-pK#om-xJ sf 3H29yۆVti7ns6dIJ̯ߒ]\^ضE̖ 'CB'mB/gr`hM걑#^'O>;؍HE|티wυ4kXHPx3xYGZ_xmqjq-XߕOG5q1hq :K6b &n/_a/L?qΊnϮpY>Y9fzNNCi;W5TE% o_1 zz2Q[]gE-`/QÑD,{-2rCxs\믟}!ȀٳloE6  ^yq>6/A)y1-ې&pp!GS&7d7Z͖7aRU_"wf"va1oEB&ϭ`6E`!t* >:zDRcGݻϹwd:FlDBu\< onp/XJ*J"8s!Lr &aIQ)DEZ$.>D4{S6ȵRFя֤ R.1=|~cC{^ [Bχ–SJ@M!&T20FXGh=H\ Z:S'6m7Fxyɹ ?㴍YżJ!,G䉇IHFcnybDdoQY;4ڽ9u0U1|nx_,,v7Jd{*/0R:B+ra:p?6Ivc5T:qBFN6Kk]"aY5zee[~U]A[PPÕW}Uuɴsřp ){̉_+3E?lez _ ]lNZF\$ܝ .rxj_H@``ȍ;\|ڥ#=#AV"`dwo)"_ a no႞ A?{ǃ"` \.T!2OIpw8k+=ɉ$ xc&lWekA nǝA#Y]aj~fmAe1KJI8=T[DXFda耔x4\B32@mx,ło~ڛTڸʱ5vﳗ:N:s(/3j^ KeTDzQ̑盗6D)N<}*[}U(uVpNMߓG1uY"xo\ρ4IX ?O]$sv}EJݚJKXgz۫-HsnĂ۵o=5;4|$LHdm :IbKu]}w?o$ sYF(*("SA ji,YfxV5~Ʒ&tY;wo D&xj[֌v@!YyD:,I?ꌞ]4C|M9[ǻLX7'$_vs4sz~,}Bq={1[m}yL~ `c)/6@͊H[^%=EMyOO0QΊL`A MF:007?Ϋi7nu¦I3Ԅqc "!~7^4HI|]+nc[:Z`gIK;o{w&&&FkVj-ym-~Pc0$Xud+1LUY+-r&,LOHO>w'yG LYYzj7~wN-ks;<5VGekE@[YEż{=)yk endstream endobj 141 0 obj <> stream xk```gMm[ C endstream endobj 17 0 obj <> stream x\ks63vLd2cIټN&>(h֖<&~I ŌIee\LL8q)32u&|fQ/E̤<ˌ33-2- ϸDj!K?rkDP,dpv=. S  tPi>.WfϮ{'hP^$Q%nr?#U(.ӛEqlqqyw\,&]T.7LrR斖'sW:ט>\ 5S3e--̞ͦ JP%Ng4-BKD5׫\uVgr/^=+ޔ|R%Urz9JjHf_;PlO.'yh6?] 9< /6Pslӛz$f#%k-BlKX$]oD3-J\t Rx^8mn >rOYV/ܯԻ"Y-:+S%cO?CgofQg:. :Ј/fT-$yR_YVgU)-d0#HըVTrX?n8Yivv0^ejc}]g*C?`(Fо\N,P[!+jem\E}km9ABx%A74eLyE^WɨF"D"6گ-y:"*mATEL{Vx2<}  o.f 2vk^Eoߊ/!!l^AXƒ7+E7ޮ[b}<-(_+m=ͨ4}r5>'PL2[Waq4\{G?ЭsR g;ƾU*a*,MqYn).Y-pj֔.g9zɈ'bvNSGپ|Y_13R+L 3j8-3^覆c9HJ [fJ4Jʹ T`PL3Ag[u&}p^t|]_:]&9Љs޷k=f2}giZ֙DO D}/]~Mh#N `ILрPDIj.tHi`2@f ]ۧ+?[>&C7 =@jh4]6$*ѵkD56hB8=bxT}mFFԭ<1 8Hρ$'/QLhj TֈҐC;&,$GU}LJwݜHz>8UX"=XTg|c7("jy #-\-P=nCh00>QUp9s.ԅrOK@(.KC_v+-v4SzbJDG&)8v A<8t5sW1g Z@T'I=EpHvm66/*+Mf/?9>g`p^ڃw'?/Nf)'𚚟~/,xdzw<Ɩ>Y|av|F q&_諠*l](#嘦q!Ͻ.Nq\TN?Vm~?e2^|\A!9%|gcl8Xbx> ~^[t6/y\F\?jlrQJԧ~h|6tSt:"*Č".]Mofo.mjlfӷO_.djܑ-&fb[LnBlɖ$~1[:[lweٝ-_G.ƻkQEPkk+N#Oїu2a,Imya-wM՝*'O߿t "D-^ K,Z,!OOr K*;UtweKEw˫wO^Dhe]p\Yhe:t:"|e aLN߁.w押ӝ+|Lԋt|;v|+Z'WddA+&oY}pt/}i-TI$"#\%,3Y6;Yg#VP-V+& ,ې#ѭ>W9{t˾jKJAf=ԕ]B݄u޸yW$i"[kV!6/&p28YIl^ez1U\%ML fS(m endstream endobj 142 0 obj <<0f6ee843b88d2230498fb6a979183931>]/Root 1 0 R/Info 2 0 R/Size 143/W[1 2 2]/Filter/FlateDecode/Length 330>> stream x-73@}''O> stream xڕZɎ#7+Ds_ >J-|_LBF2 `0zg3̖l$eMc:+v1ګy㪵ZǻOC_3~[e6cT 6FvZC>?.ƺj+h_0xb;v7}\lbaXI]Dx|FRl S7]i+U#66|8Uvs様Qe7X7lo}h7c"~NLvrdB:\7 ӯM&w|vxr2.>>d%g󦹒V;a "v5$\rm`"sV>%x*PFlԓmiNM.1 O%Wo3۪xS-iߍe*,|q]RMHklZj=WP 23o81rgƠlr_'ˠ {@ vĉ&Qr%&'ڌ/A`tOsC'd5Z8(~ n ls(۱i RӒDـHv2>oN8K ?'< Ȯ^_KS!W?#gf2HOJLI OC)b /zTL } [dɍ=@,%lr/z6Tc\4d\?!/*>~ oC82q>w޻h(=v _uz?ީYiQWrvܺsMˆM=fK5Nn@]2f21e P%*ICVb# OCP\prj'r!/|Y*ͷqlG9"Z|X\M-f-St' pw9ImCǘ4<ʇG24Y-:(#-#}Z;'Ѫ&p: CqШkia$!wP&9E҃Rj31IɾjXR&:|#}۵~(t2(9Hl"|v {;rue8GEK*7p1Y^e=)Jg쮕8%,Ay4t(joJV(i]>nX[.q:(ӧ0D9swb ws۷Qs}YExUlWmm}v7•9zTqB2x*0zy +ilH*D*\s} R/yYD"}Lu1Lo?B=! έǤ+}~ߗe}\ww]`>tf-G~qAl3f<N:5?+ejx^*^=y " endstream endobj 28 0 obj <> stream xYɎ#7 +H> [9]9DI<4`ƨ.-GA /|_^! /)]Ni񏳔KF)MJxOޥԀ1qI3;>|B!&$p*КTOHGkӳIjwc?_~C|Ih)%e*qk&m Ej8oi"7L^^=7io1WCH#Cr,Gxn3:ufHItEKL;$H ^R+ީ@ "gE`pk3Sk.' !G~[{_SBGa\ʢmpW`#D67sGw$#~`zl 63: klHS-<_w^.>ȯڏH[PS (~4VԴ~ ؏KZx̰ pZ=U{M}j4F X{:BtOSfmXb/GIU# GYB㐿qNm.Obju|F98L $:fO3x%tVGWe M' 04d8wo3Q ̞n=Ze&-9>VOv^Iv11%Xlp|)n#ʁ?䍶 쏾*HN'=gV{2$%_$P:1[Fٮ*sl{(3?|0-v0 sko6̦KY hy&~&Y7(OSֺMPf0,42˚I+_Z8k4&'6΀+k΃RJO'DF2D-#A]3#mrոdv4.~i 7r>i&#JxDXxm`-a)sBbYl!Vԧ2+6 jmו]P6]Q| g9 ouU QʂDpl iBa~@~P_բ\hի:SpXXamj'ֿMݔxd(O;Je'Z}g(Rn|v ^MÞ1ez*Xێ{بyy[\ݭ=OsV\8:?6)ĥJ"BppPNNb?sc#/R |g> L> NPx0xBV1:"@ endstream endobj 31 0 obj <> stream xZ7>"m ]uA _r"߄ \.)Up}#yp^3kOOjᏚr~}o_ӇOJM^x v:&ttqRRګ[oMJWzLXúa.D,e/_OHLwuB ,EZ3Z {]pໃR› 0uq[d-z;VC2:(l]wdx}LJvDGLWԑݪt$% ̟\cZIrJklWλ1x#>x9'e;4ϒ4v)?fUQz˧G}V5jY}N\WVB)v?u7Xie Y*< B#Fu2V5F+Rs܏g,4Hɜ[".l]|{+3MVH=L=$ >N4]̤#a5f9lB%*[*IڴPrB?tVY1{7S@/K Wrd",e^8P E L]xPB dRi)W { Hjn3GZ[鍆74v3_hHShtF. -kRZ] ̉k}8Wh2m3ܳM[1K|ACwt*5Sw B+F(߾  a4#8I_FyQW^pIayl@uI Ai: +:_”z=.BɼC)+C\z-܃~c{YW0wRI( {WcEm2%_O endstream endobj 34 0 obj <> stream xZˮ6 +QERObi dWt17UM)ɲe[~MۋC=HCɷF?h,\o3@7h˽1 q _usIinRUxϛpMߣ;_Z;׻.\5-(';v>iaoֽ9۰G]զ%lѤq){}~?-;sj`9kk?_g SzIk+Iv\Bnndu&[`ObR^ax'XYOօQI՗mXF]+[V] ]SRWp ֧tUhx!<9 úOJAE0e*FAyA΀TnAȬ4,4Ý4 AlKO6%klUp$cuo!٘sa;ZB'P Q.,\ X˭6*\k}k!]?9vD=y_#F% ;O)lo1?EH͡P8嶨2LN$L-Ĭvڎ}SND9n1,;T{=XUSTPD/Vi*1U]U]fbQU1z]Uي?D1a ὑ %搟=ڬznl]*:ax<po{ѐl teHWLJJf2@!gyIs$` yr3ʷ[ cH ;ZxyZ ZA>#( 8V&dm1wZ)#0 C9@,z|{x&amGQnYTUxV1CG89r,W/J$?^iBuưFmNǻYfQXtk%cX;xKX(P > stream xYɎ6+f" ` [9ĭr_­v \ŪWp'/om!ǜ=-vho\?+˹^9<}wϗ~dZoD?fu.NúG̊zzxYԅY=x7/! &"/UH DŽw=Fg;L~K2e9ɄЙ@(n)5S{1^a7ihxOJ\IAZojW6Lr%o1@$j qBY3n+ =箬RǔcǤi9c1ZLcgt- BBfyD#Qt@z!,sy߸h~OFx(M&u⃮~'יJ[vlkFe1~e6c&?B4L*Fhk|:a&SFҾxG܄x~ʞw3`usgbG\X.Βm R1la^`~@^AP*2wWd6t5 rSc֢jв yQ&nL&W(6FpMwb|iYd-c0/r<FDSkdN+!.)\|"5iaq[@Ʈ$k!"{z*13g||7I`+6H z!J}$Mߏf!vך0E9ϖ㡆Yy2LZUl06f?Ac8~7?ĢCCuY]Z4}2_N:}֖*.J7%t阶[@hB;Hۻ(=1J7JښPkIfs5 3> stream xW0 q$R'"@ ];u&Lv ve|<$O&=)ӓW7I_t>~vst:9={eyQʙ R$Z) Ô<]V]y-yF(|BM'Pȃ> h+[+'qHIRYꠖYҷʛB ZնEku1a-8׃XcŮom]Y$m=dIPW )Қ'vKi|yCK0;zr>YE fД--,í!1αbؐprC9ҐYCƵgp]Jsֲl閕maf$_Em 012,(E.j( *cm<͜3l~;N/簊] #Gk|T#nkuB qٴ}(d}^ힴ`D{C&epg80.s'Nw!;!ws'ukTYS8oo'>&!@kA!5y- ?fnjcWSٷeYi9*Q80CMPq0lwj1ڸ=< ?P\Է }۱7aoM6gouR?^oc#ޣny/ݝs:>Vgڜ}ՄkǮ#z=fm\ WCzotC\ 6*Y^NRҭzJi%tz}zD&W}|Rm졭B*E9ilye.ߤCvƀŋwwCEnp}:N5z۾UX;,0eIGL_<@=kؓCEە? 3!\ endstream endobj 152 0 obj <> stream x]Mk09n)_U Vj[](e$D=kCx+3o>ՂOiI[i) {.0鴑@ Qd@y~or "g; S`عˢS$e|#7|2OPsZJq@1Amd8*BQѣW /[G6qfA^JKIb):8JZJKYh)\,loY Uje2Uh] endstream endobj 153 0 obj <> stream x]݊0}\vYڂ]]Pczѷ_ ؀—93oGfȾidu+IC?ɒ؝ê50S!EG1G!͟H݁="1skQ>jWwz[eE [ݒ\ꈏ2X(+DQ,xCFd+fQ6 ^;w|򷐋NfevNRENʠ݀ 2i(= R܂@|?| _ :DvBth@Q{^bs1Si5K/ endstream endobj 154 0 obj <> stream x]M0໿".K񣚴 V+R۽8v%Cjް|̸>۫fddA#Uintojhe-{Me*[btNkKmKsVQWO,gkݫcq0枧è_lQu7z}tlqM xZR#$1|Mv })INNMO|zTo<t?{YZA;04ZPi1!1+S@96 = Ȯ`Rq8C2ArZE c/"&8"6c=8_  3SMu 5_wɧSMFk(kӾ( endstream endobj 155 0 obj <> stream x]]0+r906_ RPkAXwvǙmt c[boŁ-(6p/wq (r v׾/۳dbYOY`T}~q,&T AkVXSA@ykn1O 4(7$Kd1ky@k 5fs@*]s' {W4ȠB&< f@3 Ӏ4T!L'%iհN# ϔ`f&=.0p$ēa> stream x]]k0ů[º mw;P~z1Aute71K uIk;( wU;ӥ뱦'C[h?SP }%]gt]jz|<|&XY6[%peS5:yaSZ>WIṈXsUM"g~b[$q?@ٹ+8> stream x]_k@K)Bbn.BӖ>B+o_31MQ{űw]āNKr)=7CVfxn$[namnDGGľoε;z\&Bo֗1ݪ3{ ޝGoa3QdIYrk}Gi)4D2R,U&_Zñl,Syd@RbM 1HdAc$k P0Km"I"RE*rApmL&V Vg^zT/A@pP%&xQHJ;cB])4 gj B] o  >%)3 FY&iA],wN̠ ,r8),%#X'f'gpk #:OS0a*߳# endstream endobj 158 0 obj <> stream x]Pj0+KYX "nږn`MB&:07yfTxƫBhu: endstream endobj 159 0 obj <> stream x]n0y /;U$v !Q~4HCBbH%LXC]u1@rmscOնm5y]u:5mʫ?7miU7 K'ʋW~n׵]>W??6S?1{:˶=uj6K<5!Ӣnj>p/d>e_l>gf3O|[WwiS 1ˢG !23@xavʚ g͓Y!# 4B9mq[8x_G}׏_ endstream endobj 161 0 obj <> stream xeT}lSUڹg iˇ@% :Bֲob T~~Q>6h pD260#4cF 1x\L| H<{{/'? Tekfck72h4IhVbQTHSTI t;)kY2>=+r3>`" (5E?}"lXxi+1V!lV,).~uVƗr#\j$lFV[(4@.y6oe#4Ymn*|x;ovuXxXyv V \f;<,,+*xAq'2u+M˖T,^ ZxfwGZp5@ԂՠlJ.ٲ8E):O3>^iOcS"Up" 1?I:ݫIS Ȅ?fǪ]v|kckhf`DC D b;å۟! ,z;>N~jۺb>c1t`xlc5c2Y>M\3YՑco|=xVqWgWS:7L Q(]3@m=l6P|_8陉j_$|P}cVj۫ZP"tJDNuV}={?rOSweH)K~!yOY^yYo+_11f1Σ+'t"F6#:!Km) {l$!:WID"H4;^N;bݙ ' endstream endobj 163 0 obj <> stream xk`6  endstream endobj 165 0 obj <> stream xڥW{TWi1];cgb*V@ԶX"- !<XG \A^HLVPEkHX[iv[ҮmݽC/{v{9Μ;w}wdT65N$dqBiT馢ky#hc܇/E~S~Ϣy"H LCRGٳ `l 6{% ~YDmb}BcH6d/ צe钕*<$8xS yX|C\B֤OIiQh&h5x*N$&c/ɍzN/W4@y*Y/7iu)rSqzEܨITBuK|VcG&'(4z|b\PUCڳAA2PS%cA郦-^):vqh4ȓ:y?85ƷycX B 9,[a:, ی`{}X<`)p} A`g[Hh%Ecc3D3̸0uѿVP,u=?2D4re[}^a8>zН={[7h\- -' sSIЧW_.{E]eW)ڏ°vws᪚n;-]`iarg GB(P-⯑0v..q1f"3U="Jj~Uqq(cBKd,qz%6%S[U0ܠ8 tw)vAr; p6UrؚOHBrS;(jAi#-8,6KccڈBkJP0\>tG3q omK=Lcz譫>(KK{ܘinVHZVG͛ 1s!!;]ZY(HTxv*aUjzގAұ=/W15-DK.y"8KVn-+ȳY6u8* |qA/N 4فֻh6/}=~pVL+Cb3g9\H39Q <[Ndj.>+6O~+h‚=twwHl_Fο[(s>Eݱ9FQu2myĕkM,tնhKc1ar&M4eh:Ks^[;#09>rjoMOݖJXMVMZ ݪVPhr}Ns#E&ܧ۹t7҅b; l2kYZ()q7Ў^W/u|4P\9OC&FMp^qބ0ᚸp]p> TFqb" ':Hpץk EcnE•2@^eIn~ HP |ϟx _:JmuL6w\C#ԧg= ڃ2DzN=W𣆅!3jϚ+'?E! һNo9: أ \Kp cd"'NWMuNk" F&-h; "V[A AJnNz~ΥzPClZKՖ6fUpÒa&QX;C,.~ÛvPhIb(úС=Wgۃg.L/5u {OfdJL}ޯzǫ .H XAJ<r{ng:p陾Zo;sdγ[^k/wee~Cs&~/n  endstream endobj 167 0 obj <> stream xk``P5В  endstream endobj 169 0 obj <> stream xڅW TSW~1ˋ<|/(,.Vł&HX &A!7l  &nZiڪibs339Kr~w nQ'L寍t4-\Hp N9sr#ExLxE&K6$VX$w\mlkXpTN!%rBA@xȂHmfY&kH;c61UcPN>qV.WoӦbHroer6oUN&(#41(6J W&5:2ZMJOV)Z]i5j&R) 1!mAI(JF1gh(~'~`1+핡^.^۰͠ꔑ:6^GNG/“&ӈ7[1X@KD D"BPBE"Bh K?AK|/'4vE0d FŽld599i;u խeb`5X.?B+dpsao;ٱj^w:p^a!cQv, VO@hVŧ҅E|'F۞>f>#ܜw,y=$<V|u@̱Ԙ_c4q##Ʒo1!y421hyeIybinu7|FԵdvF"uKv0v,)_A{Mf0HBs+R@pܧj{SL4uZ{wg0iid6ZrX>1I7ӋK2ouyUd4n䦦Si/ڿ;LEyz6!hQpVHd,PA-!kL򐩒1(? L????7W*p\)Ȅ\h@3QxtLgˊo"xwOV05'+G-ڃhVᴋϠ?Wa\),oʀn -PB9#Gu240J:!KܭԮmq)5r``:8<%ns~_K(TB1 c}, WT5($gh/w1W%OAJ)9? ;vNlaB 7_݋?Xrh-0ٕ=Ua^_o=xigvh| X'!!3xlxbMg]]Lr2V(` 1faTkպ&xT,Xڲ.\ؑnÓܚdiYEP%mL(`r,/4zKwx||tfYAc/7 ZkBn3[)L؂%oQ\~AXXgfLQf=;}99 mTA4OFx8Ԕf7X=:b/J2/@ͨsJsCTI_JucI;8 j Q]!?#mI]e(ٱIĵ'H7󪹃$w _:qc۵S}P*ɠLr"‗NmX9w 5~"){3 WBwit4Wi^ph/]@d1~ޅ}18?Kr<:V˯C)EE UP".+/E;5V$s;! EiYJC(w_;aZ(v3UdHي}*L@U(~TZd5qja~f ]Rg{WJ]+,d^zats7An!-jxI [LnOHÐg*Q uz†KXa/簘/0ưAkG<%bɩyw)\J!Pl0 PK)/d6/,-Oo.*3ˆq쭓p endstream endobj 171 0 obj <> stream xk```J|0'ၫ , endstream endobj 173 0 obj <> stream xڝZxǶ^Yր1EvaWzN轻!ز&ܰ-ْF܋l6q^BQI( {+ͽIvF3s?矕st+AG _!U=Cmwr>hcԁ+9fg67H` uՍ:> NXW'{) hVlVYP1#F7K/\:fԨlR҅mRϠ҅#GH("ۤAR/G𑮒*daRP28l*maEh* tb\e* "uAȍ$9 @MUkA }5cpctFGGTWlzGHA 9P\p\$5aAAaaAAa5554=fl %ZCB 3rLab&'ZK%Sso܀8bMU }STh] I Dh: _3| Wsb8p\| #n#V,H of1nΐy~*>Y9gYi=NUI#+Dȸ,>')o) A)Ա`$AAP|q߮Z 'f@cЪiicŷrwҢ9B6ٲuǩ[X Qugٻ,}nEhB$TFՎ&0X*Gd!U)d,bTξ6Xk})n2fT߆^FM!%QNFjp& @Zʞ~!+C}PX,$(A gܱ좞 j+b-fIpUJJ#A,86DDQ\PJƑoA(ޖp?mm.־p?ލ{B4rp%v)&렉jy4 +z?缲x!&Y XV"3D,w',~N.F5s&K#|oQ<=CQ|hv;>Skj`WP̢ \@ǧFŅU5"x.=-0b!K%Qr.e}O`C𩚗2nЕ6Sy[W7/kTS<vl| ~LJ <M 'ĉSHjIOKs0뵺] s*2p/(Cv* 4~υG .?ANa1e"ŷ$8%iUdeTBW/>~oOYI3痾teQc2pHgH0iWdN,  wA]*OJQ_"qݮk=/]ۺ2NcFQMN[dzn\,' {}_˧d11r؉?u^/-DvQ` H%P߽v!R/i5, _߱4qE%3HvrDfW)4u`۸&*kP(.==12Li|"vۮ_|)4G?{aɢbgPn<+q_5лw#m$վ׈ Ju\Em%pExz^憵=eR U3ްQIY.fKI[Jtf+~@4 <4C^|*Fp! -| *F}&a>̅??eҚ׃;oےv9ꌺ A~_H~ ;ο9-wP E4]r8ny`$3S^p"9̈́:@"u9{waf|c`Ï *yѰ$PThKǃNTF>|qq@!i9?tȅ;]; coF>In-,*+[! jCa7cn׃S+U;}BrgM ~h 梮)G4THb&<$2S& iumE{p1tC̆okAu0 1 c;siڭYYN+MN-wCh'x)d_/hA|IMHL8U˼ +TT)ڐjN[1u~&6^}z}I m0Cq}2 ?+<_p}} =x3Y1pUL[;̅;κ# IP*N2 (Gؓl}饁S©Eg Lp^~~} oY$lUljBF?&J)Te'rr4(7 ]>|4.װ ̺,{[gr$6őH):8*,I~lho?=oe+pJA~qm0U -Gm̤)0"\̫~B®S2A Y؟Em s\X79P:8I취FlFqB.`/BVγVِ@# yt!M-cE<*/-;߃׳ˋ\g{=fCֵEb\H;S]$$ iB8.MN< * zN\<1m3xN%uw=UZO.7YEFcBo8Ke<;Nix6$` XAuкR aAg "@GBكxC dU(_# jSc6.IW3#ǣ`(/^jp&_+CnL j l5Ti[F;oyv~D X\NOh<>\e 5 "̝w6XmH `k 8~yLC$ĥӦ"J TOy1h&!~E[zy$o\WņJV_Id+#Jcr^i;9,U^LL Mޭjiw2B\|9wֵduѡdNOD:1Rsiide#(?Ay[`H thR1c#6-\HVFG(rT gsE* )f@Ysoe1hez05 9EUh¿+k,{{+h"ZZHk@b

E3{qw%gyzrUA =T2 y֢t]an ;fċרmckzO*r>H"Uif SYLwt\p;/w2 3 9ggK1Ða43Ҝ;' endstream endobj 175 0 obj <> stream xk``e@. e endstream endobj 177 0 obj <> stream x}X Xڞ22jFgE+nX@Q5([0J؋ MQEEv,ԽV\ګV[[koioo\n;I$g9}%#!lm D2i`bBnWb|W#Ev'T68d'I/dǐcldc B:~ /~sȴsv@P6 $#@B<ɂHh.969c̅3b7hU3<<ޞfmgT~QqiXU.ZZ HL{cUuH6">F քR$jCRbF*XW%&ũs&^DRtњ$UV]I%c4:FR569ylw nIc1zgy>]/vKNOV$&5zW1}1xb1DL!tb1GxބK,"Rb9"D'"HBK H3lؖ0HN,!&]$mk='s5-d;I}#_ o"1䧡C[>. ?}, t&\;aDI [6v5(!%o{{}GzcKk]Oszqմ17<}3^0]ϥME SN^TWT5]\vb_cW#pg(e @q"<`xHrE#<\ vt>q"^ !HܢKaFj yAP̭-DDvP;lkwb?u"sQrɲCjRģ,wO2+'4e 5sXo۬ClJoA9'pV곎@p1HvR<c", E_{Ͱ"OLD>NcO{&C=V>Ns7k8 (rg*xEUק}rIc}tix 6B+DUM fhLjUɩS[5Ƣ`2(\VF1Rab4&X:Ǡ/BLCzk)d$4gp:Tj)pྌϮ00nS&A3K;̠85}!X+Lw;L;嗜 5* O8 g`O18 T-HL`aDu{یkwJf"oW).~P NR0tw=&Ia?`Sb&1xxng?SwP[Ţbj)2 %x, )@~puh.*&98 Sм؇Cx t%:. DF-''WhdKk+4PWHn7S~zFqޫ6љul.dګ-Ek2UU]v3W? b O?KTpيC bu~#I~'a:؀ݽd*QV3#b23 'cqJFe1uE{ s/x"oZlY{\Mgs 9CS;ӕxu(m2 9ꞏkX17"G -u 1?{K+7Tc#.ԥ$0nrE׌%+~l^8K^;@=Lk -0:b[V-JR.FkDz)Gu3züBY,ÇY<'@D@C' ʦUQYQ-<5?4hLXH]?(=SlMkjj :^\zDnzHg -ރ}?cXvTTC% ,vzٱ~kzd~mau)5[AJS廳o S<<\#*Ia ~T:ʫ)/..-c@>)hVC7 . *EoZ c.yL_%{> .aLv4s[JfwmboFn亴 lw޲\9t7*,D&ciÁjYܱSi7Es缤xVHV\d*.$+3sgs* HyΈq>*dUb]<9 O5'ɪJͥ{#8V ?gfǜJay9$١vC0~(fxg׎JsJA5 endstream endobj 179 0 obj <> stream xk``P`Ts1&E endstream endobj 181 0 obj <> stream xڕX xSUھij^A,P6ed Hi6M$5MP6}9. BD\Fsyڎ:i{r{>>@ xxfaQ4H&q ?1N9a7Gtl>A)Sd'/|!"@L'h4k#5͑r!`Y 8xEP+/-nEjLhL)ݲ@}tƄFKhpRj!פ zN/U4 Ze^jbS'Wezy4A)I Jt]! A-:B˥Kz\4+.4$(htQ~j~!^2ص~lFitHA/MXǛ~ēL)b641K#!BbxxXI&$EPŞ2H͛iW`6 H.pjOҰ} KE=Y`IM3LA@i?䨱3}4pb;S}1.}<xxa@5oV|o)_E)AtfWӾbqV.`[[%0Xx_itt4HEovЇz5v:D%.alq) %z/k3yL{37?=+R|W G:I׶{ ?(O مy|᳢6vдw[6`$(XY11lhiY5z4s|1vZnhsGq{gao.)Tܫq -͠<kHQzϽ|"n`4cF3&ד}.aۛKkj[UkS&g_g@[1XA{CiQ5۪0gedDƘP>MGB$A[V?t8z+>ECRܥg -$Jtڱp*CF뒛閆Nٌ}hb gvݮ6\Oxkx0U)*#:G`L =~Nl`_.F) i_TZzoc^b6t} N N L R2,qDt' N֫4 U~;S}5-ڡ?h/խyDLuͻ g?zuD nq~>7O~R|]T6Fz=؉` {{c=sCTF&G-$5d=~: '&)>O.CK)g.!#7!iz#SGot]dcm6Asgє_xM28[3o}݄e ~rC?1<+g_ZFdG:[174ap7# MuuS<79 ?|dUZg.j) J]/YwK0iQ)Z0;ȹi2^zY~P>'9 SǸFf#FѶ18d_mm=c?NS^V0TXtf͆mL֣e&'H)eJ/7eŇK$<(<νϭp.rb[@ Zn0\.<`J)'$ucD W7w?QPtvKV"d0ž2% ` =*TI* JEc q 5,N兽mnaapv%2h(T4-΋?ċhxs1&?8<|'Ф ˞>.|NSQ,dN3!dhv:yU/4=<&bM91u] P1\kZ92᪒,ܬww慛=NBE ͢<@#ƨAkVk-.l*bs)xlq_N6H*R0yujP_uI.4"#kΉV%v=[shA C 0 *0Yi & n'50<Yx8V`%t8VpᒫD1^b|ҼZaz;jըo| 0bOG haGCh}<74`yte%yʤ}䯶 YW0m/EnX/GA3z#b?߸_óN_:;%l9#Jo@ds N|\ʼ} ȯ'i/}^eܫWV'qƖa=oȉ3C Lz:ܗy_א?'0Og#w'ZlVb3%mf[\u,u%>`*8Tr >CR׉k{B=w<9n'O\v|?%*p(GO$wt \D&zɧ]kHjTlQM.}]\xśSgGS+@xsQygT-UŠ$b'd̔LzF+nv j%K@ ;8Ty-ʄ̌\P%Yhs:ab)P+m$Z]ZE)W|Y,YT'N5pYWnn(Rq`B0gD2Dc#0xZVM8N,y n` dvgM LAAPЧdh-f/(.+(sAgǾ_@cPڽύOdS>&o2 =hIZ֪ȶX)s.WR}/(=O5Y"YVA\qeXӞMΜ.5jJewuH<5e 5'9>X/p{7&"=y~UO%i?v^6 ]+mtm^120=W`)qK&h Ml=Gkv ϛK4->#q aBgS!,lwt6C8xO\sCXǻl$͵[T3m/,-8,ZlT {)XY!sc> stream xk``0l~Ͻ.lx endstream endobj 185 0 obj <> stream x]R[Ha]#uZFg&^z)+aԓ;:zKeMu׼h%IK"Mfl!9;Áj50,>dK :#oqSʰFʒ8!@$'SIjK׸!a|6سd%s&nSxN $dpd wwlm)4.36r8yXj0\䫰UF¹(hm5$41- iq!cfz9r2^WHB` ú\YF٠!Nיa%CjD4N!t^{$:vyӻ-qzNWydU+Lu$C;B-#2ʀU%K$CTXjЏjb}R7[i@OWiGAw{k3;sR{v|hpRf:*VDXX92s>5銽 ZibQHs3cChr.x«B}{+`"-l$!p jk}c,ZևΗO5^\F6Z@;mӮX,j6bR&/; P\ 'Dx$JLp|yBƂIi ?% d endstream endobj 187 0 obj <> stream xk`M endstream endobj 189 0 obj <> stream xڵYy\׾"*T̴gR[VEբR H k6a ;}M}iq Kֶv{ x_BaΜw~B$[rnn2_Jh MvD!2 `E,T!/,J—xXM! b+'"""'"H"!䄂P D"{2uu _"?mh__0aIy~txg!c\䍹6ϱ-sqwѻ\(=/Я'Oy=+;,q,ϪD`]XeҔBo ׉QL NgzNT!-YUȚ&CŅ|9qnjŋa]#g+SVX)5o6 0?Ao's֒n#*6򐱲+a>;˲ӥT>uuGGTÉ}Lk!7Z%%"")tܱgarYk֤2 z$蘴d"YUkJ(TieBSjmo1W2)lrE]fWjk k[D?[dEØ9׎5W66;;_bLu8.q%Mbs F%I( m\%cP⢛rKٖ\eu %ܶ31Hy= FpJ8c3bGX$i厮oC-W1Izbk*Y@9\hϓVEV59[KÅ9ȶzT3WOu_UFwUzKkFU{O`[s &u% 3>3Ȫfu{{HϷ$Q=={÷m(t*&Œ?3&wtΩ":` jxǥ+~afEx.ua%ig:XkNLg,.'Yni쀥n6"j>{>Dtk gФ(٠޲N\:f51MmWaU_YgƁ7gy;%2X۹ ibiK~@etdpbȘW+hRӲ3URmURuoDm-eT2_"C,Cxsȶ M)co{ϡ]m tUlC7xlsm+}w/p5~E ֣-V> 'øu$;8 3'?Iuc WpUl{.U,ϒݠ,.x 2\bFZ6įM3Ӱp庁|5}*R)@[h)Lq-y{3Kl.yjq9bBEIsaI=rѓ7_ǣ+.\}l|͟;WDnϕG{J^㎨=V}n/ /߸,x+DGB\9 廹.pRLu {def^ Boyo]"N赭/uS%lT+2fh"i) OqG\;* k4"z:,0"i\!H`5kaV^2yj,xWQݱ6uSnVD$4a[K=oRHդGK*%)ҏ]{3U:/zEYw PQr@K22#`2&V6p҂ʂROy}>ўޖԊ˛ *;MR˱=ڀgA#Gμ{h:ƴS=)C*bn]QVw,.e:EZ,XSj6\o,Dp%ZB?J~K @%pj"`.YiCa(>[)_*hwwz5@}O+e7 .&AÓq1ţN '-$!Se/>W~\GNNε-5( ŵbmT=A9DЯO>;0d8_{A!{ a5y|~H1 w 2vvn3`>ydnYm8@6}E~y FY@|,M7 V`] H*w=crBx?4Z+}xQհ<կ7 >68UpB>"k65ٹt͉z`qze~n c-oP -.z|O /DJ"t fy!wdbx[Jhz!fb.v|.Y3Xj:QNETmhHrc_Ar"Pa*ח3ŕl[0u4ǒ9G=s b!U7GY;.zCQmK y`A/t[\Z+>>3w<7ݙ3"<6mLzќMu URtdR]iWz>9ܪTT^**Y-60P9M0  *Vz,Y;?|O.jg 8 $S;mjV9 ))(xTu&7!G.UdV4;Ζ a寰@S*yΒ[X2@i4q$8^uG/{MGB^넊`0ZJ_X.0 JZYcW\G{k#<&XbP?@Fmv ]]( m0w,|SC0HfTm]JVzϳVJnx%n^٤on-,4Nj3폌OQx"nN4K>^jڃ%Fm3Rտg xD/Nf1lk LsaM?v.|eJʱXc4&1mzqVXVFޡ;sQFNH;v}8)ueԃk-߽~Dx06OT'WvNR4$pR.6EWj07}somFo@'$wۛfZ}K5(.W=AdFp'@y^O/܅V53 u]CL>V`PQB-z(8hq׀'O\U3L(>P7D\[\fRtڱ|=^q,HvVת뷿|S@+PncunnM$c#,vh8E{Eh.1;G XyGrqL-?}]yIRYsP?f"Ynؓ_&D޲df%fcw9 =y69_?B#atgph<\^v% Vk3 :f5}si":b=75e˯I!`¹x5 TI;TEtUWB膙iXWW)A]/8 앥_@q@v'؆odS3 LC4F\W~2-@N{_'y~'EҚ*O}Kl35jU^lvX2UIu EU&_?\tVz[gqdȈSRa^2by>A?mPa3GZpk܄$2wuy;B:?Aoo  MMN8굯 PKI`}*`5J p+[.xޕ^KӲ^ͺ:^VJQGThmx62V:KHmCHY1;*2Iu e6ru· l!n@aR|Ae ~z#ucӔO3!Ճ@^^Uxֹ6X+N0dꤶQpqv?4=qR4>xTSNR'_Z*A_.![6B~2%",!\-{܈Osɬc)O. cWMeespZ|rYɩG녪"+ %2H# 2kr;P^>qdel~pYxF < a9\ߏ .vɔ4:Q =yĉ0Ocٲ&TpE9A]R!.KDx*ΐ((l)*wqSYAE3Xq A m?oⲂ2t;FYFZG3Wa]ɩ>Xk,}wwha?J0 endstream endobj 191 0 obj <> stream xk``[yUYYZ ( s endstream endobj 17 0 obj <> stream x]ks7~Tk--l+UG8-ҲʢKӘ`Ûl xt7N4JʻBBkY(]`  rRWT) 4IfZ#Ee͂+*$im - ]V¢ #!e,Ro ׅRPBCf@}c|`Fro *| lz!M х1H, 'tE߬DV!uHO}H.4.Ed*҅~FE}G7Ȓ(<[" hr }E;=8t&z< hao@{є(@xf,QU MܢB-2Q?FkV*^X fAPB 5L .ӆd8 Ȣav* s+`=d^P,'t:a2'1!(xbngHfyxZ6G 9G BEtoQb<7&K i<5dqivk#u!l @NtI|K&y,U$+D *C`Pְz(U$;*b ADL\ThÁcjAYM[9j0H@Ee>e9y|zLޞǧ7y:[,ϦAY&'&7yidrr>ΎFM,7S0Vc3Xya=z__W&L>O'ǟ6O4V}c`cˡe91OƘY&p (7ZYe1΍ytblu0B~y|P>q+8lT xH~Vogw62yR&-[[MTpE>݉FV]o໶ZPQ/?oýf r4^hv]Zrb譐*]#Fn摌<3zP9^OjXUibG&7C\VzZPK(Vӡ(vWߏ멎Rxe͉Qeb{ T2FƷ#z`캤-vdU+ˍ,*NȚ0v7KSR.=aCK}+C5QobRש^w$u95\vLԱ˱\K2H.=~[Ra[.Ǣoȷ]8E9wI_{MޑdLQh(B "ߥȎBAF =C loGK%n{~Ļٝ0Uj7(8ᓇ2MZhCUob?sX c yz~r2*>)G{(I˭ =BѱlQb(,{ʱ)SOXz/bnٷ.K\q|!B 5UOL`#_9[_,vcZX|~|O9еbmXho/aS|r OOLTYoeXz)$z%nO{cmo`1J%%5Oq&+kϔ_=vw\hN㺮[\o8xs|qz)vyTm_[MgM~f12;Qjh0]oj/4;14r IՀsuu':=CEEPn̻HXle\@U_CںtE%*}8qTy.<:y dgVnw\|q6uhPmGgn?̷U?CRɻ#{z30mJώHݱ=3"Jψܛ"Z' WO=?u؃3JxhOC]9zwyId=s0D9t^gDqy-x}{-DĪe3P">z)"/bE0yݾcS|=|&~L3;v?ʟcPzѼl^k/Й+eX,@g:n\ޗim~/ӽm?t"ktW^]!g[9誷"߳tϦ qQGm9s:y~Ose- G׳Ƌutu+ڿ^MJ%jyB~mOw%#Uk-[fz2ʥ>mϷШcN>n8v??C\'>T4&j˳-~9_PZ0Jl $$BEJ_Y>y,:j^>ߜo:x׫ო!|3?Yq0T300̛)FςǙvM@FOdgdFVS'Lrj]d剙q 1vf-(]K3u/-쀉*E)7څFSGHUl]i4,J!fN|gFb;񕑡 M4tWM|.LXh6fj<.f<<>'lȐmq|c!@k[]wz8yFF㼉*(UvcsMF3/XC#GYo 3Y,' *+DNx7n쬌4>ǛU=PQLOl`DD S!U%PO$gZ\|"=13'(0[܆i-=TPLo9N'#-H]fRKG ; =EVI|#cC44[7Vz!( /o0{o$&f69y|dYɳXp+&I^ b#V, [xzK,Uly/k]EbEWlf-6a "o,rl◲ `KNPllyW5OV'oQʼnPq7pY ߸W!IpEY]X00l ;lqr[:JH=*5; ,Ň)[K,F,P X%T)R JNPHy.f8]~~9wY SETJϑ<5b584a56bf5fdaa37bebe1130793c4a1>]/Root 1 0 R/Info 2 0 R/Size 193/W[1 2 2]/Filter/FlateDecode/Length 430>> stream x%Y7qWhȒ&Sc-21IF%[;-RaE"TL qn<N?{cB0`8Xϰ_O16oUL^1wM!H #bs;3F JKg@DY8`\KP1`?BdL8YBp.ȅݐn8 Ǡ p {`/ 8 N8 C$C%T9HTH05jap57nB#܂&h܆h6p}N?tCB!gifE"{"ag]_OkM]+ ZRWkN*[QAҵ5/2(#)Z$ZִmjHУZ~6?W endstream endobj startxref 47663 %%EOF rvirding-luerl-772b25e/doc_legacy/src/0000775000232200023220000000000015114130670020244 5ustar debalancedebalancervirding-luerl-772b25e/doc_legacy/src/luerl_old.3.md0000664000232200023220000001304315114130670022711 0ustar debalancedebalance% luerl_old(3) % Jean Chassoul, Robert Virding % 2018-2024 # Name luerl - The old original interface to the Luerl system # Interface functions The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). ## Spec Definitions **Binary** means an Erlang binary string. **Chunks** means a portion of precompiled bytecode. **State** means a Lua State, this *is* a Lua VM instance. **Path** means a file system path and file name. **KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. **Keys** means Lua table keys, the keys of a key-value structure. ## Functions **eval** and **do** functions differ only in what they return. The **do** functions return results and a new Lua State, the **eval** functions return a tuple starting on 'ok' or 'error', then the result, or cause of error. do --> {Result, State} eval --> {ok, Result} | {error, Reason} ### **`luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}.`** Evaluate a Lua expression passed in as a string or binary, and return its result. ### **`luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}.`** Load and execute a file, and return the result. ### **`luerl:do(String|Binary|Form, State) -> {Result, NewState}.`** Evaluate a Lua expression and return its result, and the new Lua State. ### **`luerl:dofile(Path, State) -> {Result, NewState}.`** Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). ### **`luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}.`** Parse a Lua chunk as string or binary, and return a compiled chunk ('form'). ### **`luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}.`** Parse a Lua file, and return a compiled chunk ('form'). ### **`luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}.`** Search Path until the file FileName is found. Parse the file and return a compiled chunk ('form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. ### **`luerl:load_module(KeyPath, ErlangModule, State) -> State.`** Load `ErlangModule` and install its table at `KeyPath` which is encoded. ### **`luerl:load_module1(KeyPath, ErlangModule, State) -> State.`** Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded ### **`luerl:init() -> State.`** Get a new Lua State = a fresh Lua VM instance. ### **`luerl:call(Form, Args, State) -> {Result,State}`** ### **`luerl:call_chunk(Form, Args, State) -> {Result,State}`** Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. ### **`luerl:call_function(KeyPath, Args, State) -> {Result,NewState}`** Call a function already defined in the state. `KeyPath` is a list of names to the function. `KeyPath`, `Args` and `Result` are automatically encoded/decoded. ### **`luerl:call_function1(KeyPath, Args, State) -> {Result,NewState}`** Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath`, `Args` and `Result` are **NOT** encoded/decoded. ### **`luerl:call_method(MethPath, Args, State) -> {Result,NewState}.`** Call a method already defined in the state. `MethPath` is a list of names to the method. `MethPath`, `Args` and `Result` are automatically encoded/decoded. ### **`luerl:call_method1(MethPath, Args, State) -> {Result,NewState}`** Call a method already defined in the state. `MethPath` is a list of keys to the method. `Keys`, `Args` and `Result` are **NOT** encoded/decoded. ### **`luerl:stop(State) -> GCedState.`** Garbage collects the state and (todo:) does away with it. ### **`luerl:gc(State) -> State.`** Runs the garbage collector on a state and returns the new state. ### **`luerl:set_table(KeyPath, Value, State) -> State.`** Sets a value inside the Lua state. Value is automatically encoded. ### **`luerl:set_table1(KeyPath, Value, State) -> State.`** Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. ### **`luerl:get_table(KeyPath, State) -> {Result,State}.`** Gets a value inside the Lua state. `KeyPath` and `Result` are automatically encoded. ### **`luerl:get_table1(KeyPath, State) -> {Result,State}.`** Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. You can use this function to expose an function to the Lua code by using this interface: `fun(Args, State) -> {Results, State}` Args and Results must be a list of Luerl compatible Erlang values. rvirding-luerl-772b25e/doc_legacy/src/luerl.3.md0000664000232200023220000002167315114130670022063 0ustar debalancedebalance% luerl(3) % Jean Chassoul, Robert Virding % 2018-2024 # Name luerl - The basic interface to the Luerl system # Interface functions - New Version The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). ## Spec Definitions **Binary** means an Erlang binary string. **Chunks** means a portion of precompiled bytecode. **State** means a Lua State, this *is* a Lua VM instance. **Path** means a file system path and file name. **KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. **Keys** means Lua table keys, the keys of a key-value structure. **CompileOptions** means a list of compiler options. Currently supported options are 'return', which returns the errors and warnings, and 'report' which will log the errors and warnings. **LuaCallReturn = {ok, Result, State} | {lua_error, Error, State}** This is the return value from evaluating a Lua call. ## Functions ### **`luerl:init() -> State`** Get a new Lua State = a fresh Lua VM instance. ### **`luerl:gc(State) -> State`** Runs the garbage collector on a state and returns the new state. ### **`luerl:load(String|Binary[, CompileOptions], State) -> {ok, Function, State} | CompileError`** Parse a Lua chunk as string or binary, and return a compiled chunk ('form'). ### **`luerl:loadfile(FileName[, CompileOptions], State) -> {ok, Function, State} | CompileError`** Parse a Lua file, and return a compiled chunk ('form'). ### **`luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}`** Search Path until the file FileName is found. Parse the file and return a compiled chunk ('form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. ### **`luerl:load_module(KeyPath, ErlangModule, State) -> State`** Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded. ### **`luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) -> State`** Load `ErlangModule` and install its table at `KeyPath` which is encoded. ### **`luerl:do(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError`** Evaluate a Lua expression and return its result which is **NOT** decoded, and the new Lua State. ### **`luerl:do_dec(String|Binary|Form, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError`** Evaluate a Lua expression and return its result which is automatically decoded, and the new Lua State. ### **`luerl:dofile(Path, State) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError`** Load and execute the Lua code in the file and return its result which is **NOT** decoded, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). ### **`luerl:dofile_dec(Path[, State]) -> {ok, Result, NewState} | {lua_error, Error, State} | CompileError`** Load and execute the Lua code in the file and return its result which is automatically decoded, and the new Lua State. ### **`luerl:call(FuncRef, ArgRefs, State) -> {ok, Result, State}`** ### **`luerl:call_chunk(FuncRef, ArgRefs, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. ### **`luerl:call_function(FuncRef | FuncPath, ArgRefs, State] -> {ok, Result, State} | {lua_error, Error, State}`** Call a function already defined in the state. `Result` is **NOT** decoded. ### **`luerl:call_function_enc(KeyPath, Args, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath` and `Args` are automatically encoded, while `Result` is **NOT** decoded. ### **`luerl:call_function_dec(KeyPath, Args, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath` and `Args` are automatically encoded and `Result` is automatically decoded. ### **`luerl:call_method(ObjRef, Method, ArgRefs, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a method already defined in the state. `Result` is **NOT** decoded. ### **`luerl:call_method_enc(KeyPath, Method, Args, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a method already defined in the state. `KeyPath` is a list of keys to the method. `KeyPath`, `Method` and `Args` are automatically encoded, while `Result` is **NOT** decoded. ### **`luerl:call_method_dec(KeyPath, Method, Args, State) -> {ok, Result, State} | {lua_error, Error, State}`** Call a method already defined in the state. `KeyPath` is a list of keys to the method. `KeyPath`, `Method` and `Args` are automatically encoded and `Result` is automatically decoded. ### **`luerl:get_table_keys(KeyPath, State) -> {ok, Result, State} | {lua_error, Error, State}`** Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. ### **`luerl:get_table_keys_dec(KeyPath, State) -> {ok, Result, State} | {lua_error, Error, State}`** Gets a value inside the Lua state. `KeyPath` is automatically encoded and `Result` is decoded. ### **`luerl:set_table_keys(KeyPath, Value, State) -> {ok,State} | {lua_error, Error, State}`** Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. ### **`luerl:set_table_keys_dec(KeyPath, Value, State) -> {ok, Result, State} | {lua_error, Error, State}`** Sets a value inside the Lua state. `KeyPath` and `Value` are automatically encoded and `Result` is decoded. ### **`luerl:get_table_key(Table, Key, State) -> {ok, Result, State} | {lua_error, Error, State}`** Gets the value of a key in a table. `Table` and `Key` are **NOT** encoded and `Result` is **NOT** decoded. ### **`luerl:set_table_key(Table, Key, Value, State) -> {ok, State} | {lua_error, Error, State}`** Sets the value of a key in a table. `Table`, `Key` and `Value` are **NOT** encoded. ### **`luerl:get_stacktrace(State) -> [{FuncName,{file,FileName},{line,Line}}]`** Return a stack trace of the current call stack in the state. ### **`luerl:encode(Term, State) -> {LuerlTerm,State}`** Encode the Erlang representation of a term into Luerl form updating the state when necessary. ### **`luerl:encode_list([Term], State) -> {[LuerlTerm],State}`** Encode a list of Erlang term representations into a list of Luerl forms updating the state when necessary. ### **`luerl:decode(LuerlTerm, State) -> Term`** Decode a term in the Luerl form into its Erlang representation. ### **`luerl:decode_list([LuerlTerm], State) -> [Term]`** Decode a list of Luerl terms into a list of Erlang representations. ### **`luerl:put_private(Key, Term, State) -> State.`** Puts a private value under key that is not exposed to the runtime. ### **`luerl:get_private(Key, State) -> Term.`** Get a private value for the given key. ### **`luerl:delete_private(Key, State) -> Term.`** Deletes the private value for the given key. ## Passing String in the Erlang Shell Here we are going to look at passing in command strings into the ``luerl:do/2`` function, especially when these strings contain Lua strings. The problem is to make sure that the strings in the Lua commands are processes correctly. First just just doing it from the standard Lua shell as the source of truth: ``` >return "aéb\235c" aéb?c >string.byte("aéb\235c", 1, 20) 97 195 169 98 235 99 ``` Now doing it from Erlang where I need to use \\\\ to get a \\ into the string: ``` 1> St = luerl:init(), ok. ok 2> f(S), S = "return 'aéb\\235c'". [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] 3> f(R), {ok,R,_} = luerl:do(S, St), io:write(R). [<<97,195,169,98,235,99>>] ``` Now doing it from Elixir using the ``~C`` sigil to get the handling of the string right: ``` iex(1)> st = Luerl.init(); :ok :ok iex(4)> s = ~C"return 'aéb\235c'" [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] iex(3)> {ok,r,_} = Luerl.do(st, s); r [<<97, 195, 169, 98, 235, 99>>] ``` So it works as expected. We use ``io:write`` to write out the characters in the binary without any parsing or trying to be smart. rvirding-luerl-772b25e/doc_legacy/src/luerl_sandbox.3.md0000664000232200023220000000655015114130670023576 0ustar debalancedebalance% luerl_sandbox(3) % Robert Virding % 2023 # Name luerl_sandbox - Fuctions for sandboxing Luerl evaluation # Interface Functions The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). ## Spec Definitions **Binary** means an Erlang binary string. **Chunks** means a portion of precompiled bytecode. **State** means a Lua State, this *is* a Lua VM instance. **Path** means a file system path and file name. **KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. **Keys** means Lua table keys, the keys of a key-value structure. ## Functions ### **` init() -> State.`** ### **` init([ State | TablePaths]) -> State.`** ### **` init(State, TablePaths) -> State.`** Create a new sandboxed state. If a state is given as an argument then that state will be used otherwise a new default be be generated. ``TablePaths`` is a list of paths to functions which will be blocked. If none is given then the default list will be used. ### **`run(String | Binary) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, State) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, Flags, State) -> {Result, State} | {error, Reason}.`** Spawn a new process which runs the string `String` in `State` where the default sandbox state will be used if none is given. `Flags` is a map or keyword list which can contain the following fields #{max_time => MaxTime, max_reductions => MaxReds, spawn_opts => SpawnOpts} `MaxReds` limits the number of reductions and `MaxTime` (default 100 msecs) the time to run the string, `SpawnOpts` are spawn options to the process running the evaluation. ### **`run(String | Binary) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, State) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, State, [ MaxReds | Flags ]) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, State, MaxReds, Flags) -> {Result, State} | {error, Reason}.`** ### **`run(String | Binary, State, MaxReds, Flags, Timeout) -> {Result, State} | {error, Reason}.`** This is the old interface to run. It still works but the new interface is recommended. Spawn a new process which runs the string `String` in `State` where the default sandbox state will be used if none is given. `MaxReds` limits the number of reductions and `TimeOut` (default 100 msecs) the time to run the string, `Flags` are spawn options to the process running the evaluation. rvirding-luerl-772b25e/doc_legacy/luerl_old.txt0000664000232200023220000001412215114130670022177 0ustar debalancedebalanceluerl_old(3) Library Functions Manual luerl_old(3) Name luerl - The old original interface to the Luerl system Interface functions The Lua State parameter is the state of a Lua VM instance. It must be created with the luerl:init() call and be carried from one call to the next. As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key _G which references the top-level table. Note that Lua Chunks (see definition below) can travel between differ‐ ent States. They are precompiled bits of code, independent of State. That you can ‘carry around’ this is no unique to Luerl but a low-level implementation detail of the standard Lua language ⟨https://lua.org⟩, for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ ual.html#3.3.2⟩ the official Lua 5.3 reference manual ⟨https://www.lua.org/manual/5.3/manual.html⟩. Spec Definitions Binary means an Erlang binary string. Chunks means a portion of precompiled bytecode. State means a Lua State, this is a Lua VM instance. Path means a file system path and file name. KeyPath means an Erlang list of atoms representing nested names, e.g. [table,pack] for table.pack. Keys means Lua table keys, the keys of a key-value structure. Functions eval and do functions differ only in what they return. The do func‐ tions return results and a new Lua State, the eval functions return a tuple starting on ‘ok’ or ‘error’, then the result, or cause of error. do --> {Result, State} eval --> {ok, Result} | {error, Reason} luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}. Evaluate a Lua expression passed in as a string or binary, and return its result. luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. Load and execute a file, and return the result. luerl:do(String|Binary|Form, State) -> {Result, NewState}. Evaluate a Lua expression and return its result, and the new Lua State. luerl:dofile(Path, State) -> {Result, NewState}. Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do(“return dofile(‘File‐ Name’)”). luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,New‐ State} | {error, Reason}. Parse a Lua chunk as string or binary, and return a compiled chunk (‘form’). luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}. Parse a Lua file, and return a compiled chunk (‘form’). luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}. Search Path until the file FileName is found. Parse the file and re‐ turn a compiled chunk (‘form’). If Path is not given then the path de‐ fined in the environment variable LUA_LOAD_PATH is used. luerl:load_module(KeyPath, ErlangModule, State) -> State. Load ErlangModule and install its table at KeyPath which is encoded. luerl:load_module1(KeyPath, ErlangModule, State) -> State. Load ErlangModule and install its table at KeyPath which is NOT encoded luerl:init() -> State. Get a new Lua State = a fresh Lua VM instance. luerl:call(Form, Args, State) -> {Result,State} luerl:call_chunk(Form, Args, State) -> {Result,State} Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. luerl:call_function(KeyPath, Args, State) -> {Result,NewState} Call a function already defined in the state. KeyPath is a list of names to the function. KeyPath, Args and Result are automatically en‐ coded/decoded. luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} Call a function already defined in the state. KeyPath is a list of keys to the function. KeyPath, Args and Result are NOT encoded/de‐ coded. luerl:call_method(MethPath, Args, State) -> {Result,NewState}. Call a method already defined in the state. MethPath is a list of names to the method. MethPath, Args and Result are automatically en‐ coded/decoded. luerl:call_method1(MethPath, Args, State) -> {Result,NewState} Call a method already defined in the state. MethPath is a list of keys to the method. Keys, Args and Result are NOT encoded/decoded. luerl:stop(State) -> GCedState. Garbage collects the state and (todo:) does away with it. luerl:gc(State) -> State. Runs the garbage collector on a state and returns the new state. luerl:set_table(KeyPath, Value, State) -> State. Sets a value inside the Lua state. Value is automatically encoded. luerl:set_table1(KeyPath, Value, State) -> State. Sets a value inside the Lua state. KeyPath and Value are NOT encoded. luerl:get_table(KeyPath, State) -> {Result,State}. Gets a value inside the Lua state. KeyPath and Result are automati‐ cally encoded. luerl:get_table1(KeyPath, State) -> {Result,State}. Gets a value inside the Lua state. KeyPath and Result are NOT en‐ coded/decoded. You can use this function to expose an function to the Lua code by us‐ ing this interface: fun(Args, State) -> {Results, State} Args and Results must be a list of Luerl compatible Erlang values. AUTHORS Jean Chassoul, Robert Virding. 2018-2024 luerl_old(3) rvirding-luerl-772b25e/doc_legacy/man/0000775000232200023220000000000015114130670020230 5ustar debalancedebalancervirding-luerl-772b25e/doc_legacy/man/luerl_old.30000664000232200023220000001462015114130670022300 0ustar debalancedebalance.\" Automatically generated by Pandoc 3.6 .\" .TH "luerl_old" "3" "2018\-2024" "" .SH Name luerl \- The old original interface to the Luerl system .SH Interface functions The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. It must be created with the \f[B]luerl:init()\f[R] call and be carried from one call to the next. .PP As it is possible in Lua to create self\-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key \f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. .PP Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can `carry around' this is no unique to Luerl but a low\-level implementation detail of the standard Lua \c .UR https://lua.org language .UE \c , for more on chunks \c .UR https://www.lua.org/manual/5.3/manual.html#3.3.2 read .UE \c \ the official Lua 5.3 \c .UR https://www.lua.org/manual/5.3/manual.html reference manual .UE \c \&. .SS Spec Definitions \f[B]Binary\f[R] means an Erlang binary string. .PD 0 .P .PD \f[B]Chunks\f[R] means a portion of precompiled bytecode. .PD 0 .P .PD \f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. .PD 0 .P .PD \f[B]Path\f[R] means a file system path and file name. .PD 0 .P .PD \f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing nested names, e.g.\ [table,pack] for table.pack. .PD 0 .P .PD \f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. .SS Functions \f[B]eval\f[R] and \f[B]do\f[R] functions differ only in what they return. The \f[B]do\f[R] functions return results and a new Lua State, the \f[B]eval\f[R] functions return a tuple starting on `ok' or `error', then the result, or cause of error. .IP .EX do \-\-> {Result, State} eval \-\-> {ok, Result} | {error, Reason} .EE .SS \f[B]\f[CB]luerl:eval(String|Binary|Form, State) \-> {ok, Result} | {error, Reason, StackTrace}.\f[B]\f[R] Evaluate a Lua expression passed in as a string or binary, and return its result. .SS \f[B]\f[CB]luerl:evalfile(Path, State) \-> {ok, Result} | {error, Reason, StackTrace}.\f[B]\f[R] Load and execute a file, and return the result. .SS \f[B]\f[CB]luerl:do(String|Binary|Form, State) \-> {Result, NewState}.\f[B]\f[R] Evaluate a Lua expression and return its result, and the new Lua State. .SS \f[B]\f[CB]luerl:dofile(Path, State) \-> {Result, NewState}.\f[B]\f[R] Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). .SS \f[B]\f[CB]luerl:load(String|Binary[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}.\f[B]\f[R] Parse a Lua chunk as string or binary, and return a compiled chunk (`form'). .SS \f[B]\f[CB]luerl:loadfile(FileName[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}.\f[B]\f[R] Parse a Lua file, and return a compiled chunk (`form'). .SS \f[B]\f[CB]luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) \-> {ok,Function,FullName,State} | {error, Reason}.\f[B]\f[R] Search Path until the file FileName is found. Parse the file and return a compiled chunk (`form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. .SS \f[B]\f[CB]luerl:load_module(KeyPath, ErlangModule, State) \-> State.\f[B]\f[R] Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] which is encoded. .SS \f[B]\f[CB]luerl:load_module1(KeyPath, ErlangModule, State) \-> State.\f[B]\f[R] Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] which is \f[B]NOT\f[R] encoded .SS \f[B]\f[CB]luerl:init() \-> State.\f[B]\f[R] Get a new Lua State = a fresh Lua VM instance. .SS \f[B]\f[CB]luerl:call(Form, Args, State) \-> {Result,State}\f[B]\f[R] .SS \f[B]\f[CB]luerl:call_chunk(Form, Args, State) \-> {Result,State}\f[B]\f[R] Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. .SS \f[B]\f[CB]luerl:call_function(KeyPath, Args, State) \-> {Result,NewState}\f[B]\f[R] Call a function already defined in the state. \f[CR]KeyPath\f[R] is a list of names to the function. \f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are automatically encoded/decoded. .SS \f[B]\f[CB]luerl:call_function1(KeyPath, Args, State) \-> {Result,NewState}\f[B]\f[R] Call a function already defined in the state. \f[CR]KeyPath\f[R] is a list of keys to the function. \f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] encoded/decoded. .SS \f[B]\f[CB]luerl:call_method(MethPath, Args, State) \-> {Result,NewState}.\f[B]\f[R] Call a method already defined in the state. \f[CR]MethPath\f[R] is a list of names to the method. \f[CR]MethPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are automatically encoded/decoded. .SS \f[B]\f[CB]luerl:call_method1(MethPath, Args, State) \-> {Result,NewState}\f[B]\f[R] Call a method already defined in the state. \f[CR]MethPath\f[R] is a list of keys to the method. \f[CR]Keys\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] encoded/decoded. .SS \f[B]\f[CB]luerl:stop(State) \-> GCedState.\f[B]\f[R] Garbage collects the state and (todo:) does away with it. .SS \f[B]\f[CB]luerl:gc(State) \-> State.\f[B]\f[R] Runs the garbage collector on a state and returns the new state. .SS \f[B]\f[CB]luerl:set_table(KeyPath, Value, State) \-> State.\f[B]\f[R] Sets a value inside the Lua state. Value is automatically encoded. .SS \f[B]\f[CB]luerl:set_table1(KeyPath, Value, State) \-> State.\f[B]\f[R] Sets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. .SS \f[B]\f[CB]luerl:get_table(KeyPath, State) \-> {Result,State}.\f[B]\f[R] Gets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are automatically encoded. .SS \f[B]\f[CB]luerl:get_table1(KeyPath, State) \-> {Result,State}.\f[B]\f[R] Gets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] encoded/decoded. .PP You can use this function to expose an function to the Lua code by using this interface: \f[CR]fun(Args, State) \-> {Results, State}\f[R] .PP Args and Results must be a list of Luerl compatible Erlang values. .SH AUTHORS Jean Chassoul, Robert Virding. rvirding-luerl-772b25e/doc_legacy/man/luerl_sandbox.30000664000232200023220000000756215114130670023167 0ustar debalancedebalance.\" Automatically generated by Pandoc 3.6 .\" .TH "luerl_sandbox" "3" "2023" "" .SH Name luerl_sandbox \- Fuctions for sandboxing Luerl evaluation .SH Interface Functions The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. It must be created with the \f[B]luerl:init()\f[R] call and be carried from one call to the next. .PP As it is possible in Lua to create self\-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key \f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. .PP Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can `carry around' this is no unique to Luerl but a low\-level implementation detail of the standard Lua \c .UR https://lua.org language .UE \c , for more on chunks \c .UR https://www.lua.org/manual/5.3/manual.html#3.3.2 read .UE \c \ the official Lua 5.3 \c .UR https://www.lua.org/manual/5.3/manual.html reference manual .UE \c \&. .SS Spec Definitions \f[B]Binary\f[R] means an Erlang binary string. .PD 0 .P .PD \f[B]Chunks\f[R] means a portion of precompiled bytecode. .PD 0 .P .PD \f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. .PD 0 .P .PD \f[B]Path\f[R] means a file system path and file name. .PD 0 .P .PD \f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing nested names, e.g.\ [table,pack] for table.pack. .PD 0 .P .PD \f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. .SS Functions .SS \f[B]\f[CB]init() \-> State.\f[B]\f[R] .SS \f[B]\f[CB]init([ State | TablePaths]) \-> State.\f[B]\f[R] .SS \f[B]\f[CB]init(State, TablePaths) \-> State.\f[B]\f[R] Create a new sandboxed state. If a state is given as an argument then that state will be used otherwise a new default be be generated. \f[CR]TablePaths\f[R] is a list of paths to functions which will be blocked. If none is given then the default list will be used. .SS \f[B]\f[CB]run(String | Binary) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, Flags, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] Spawn a new process which runs the string \f[CR]String\f[R] in \f[CR]State\f[R] where the default sandbox state will be used if none is given. \f[CR]Flags\f[R] is a map or keyword list which can contain the following fields .IP .EX #{max_time => MaxTime, max_reductions => MaxReds, spawn_opts => SpawnOpts} .EE .PP \f[CR]MaxReds\f[R] limits the number of reductions and \f[CR]MaxTime\f[R] (default 100 msecs) the time to run the string, \f[CR]SpawnOpts\f[R] are spawn options to the process running the evaluation. .SS \f[B]\f[CB]run(String | Binary) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, State, [ MaxReds | Flags ]) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, State, MaxReds, Flags) \-> {Result, State} | {error, Reason}.\f[B]\f[R] .SS \f[B]\f[CB]run(String | Binary, State, MaxReds, Flags, Timeout) \-> {Result, State} | {error, Reason}.\f[B]\f[R] This is the old interface to run. It still works but the new interface is recommended. Spawn a new process which runs the string \f[CR]String\f[R] in \f[CR]State\f[R] where the default sandbox state will be used if none is given. \f[CR]MaxReds\f[R] limits the number of reductions and \f[CR]TimeOut\f[R] (default 100 msecs) the time to run the string, \f[CR]Flags\f[R] are spawn options to the process running the evaluation. .SH AUTHORS Robert Virding. rvirding-luerl-772b25e/doc_legacy/man/luerl.30000664000232200023220000002433315114130670021444 0ustar debalancedebalance.\" Automatically generated by Pandoc 3.6 .\" .TH "luerl" "3" "2018\-2024" "" .SH Name luerl \- The basic interface to the Luerl system .SH Interface functions \- New Version The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. It must be created with the \f[B]luerl:init()\f[R] call and be carried from one call to the next. .PP As it is possible in Lua to create self\-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key \f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. .PP Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can `carry around' this is no unique to Luerl but a low\-level implementation detail of the standard Lua \c .UR https://lua.org language .UE \c , for more on chunks \c .UR https://www.lua.org/manual/5.3/manual.html#3.3.2 read .UE \c \ the official Lua 5.3 \c .UR https://www.lua.org/manual/5.3/manual.html reference manual .UE \c \&. .SS Spec Definitions \f[B]Binary\f[R] means an Erlang binary string. .PD 0 .P .PD \f[B]Chunks\f[R] means a portion of precompiled bytecode. .PD 0 .P .PD \f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. .PD 0 .P .PD \f[B]Path\f[R] means a file system path and file name. .PD 0 .P .PD \f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing nested names, e.g.\ [table,pack] for table.pack. .PD 0 .P .PD \f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. .PP \f[B]CompileOptions\f[R] means a list of compiler options. Currently supported options are `return', which returns the errors and warnings, and `report' which will log the errors and warnings. .PP \f[B]LuaCallReturn = {ok, Result, State} | {lua_error, Error, State}\f[R] .PD 0 .P .PD This is the return value from evaluating a Lua call. .SS Functions .SS \f[B]\f[CB]luerl:init() \-> State\f[B]\f[R] Get a new Lua State = a fresh Lua VM instance. .SS \f[B]\f[CB]luerl:gc(State) \-> State\f[B]\f[R] Runs the garbage collector on a state and returns the new state. .SS \f[B]\f[CB]luerl:load(String|Binary[, CompileOptions], State) \-> {ok, Function, State} | CompileError\f[B]\f[R] Parse a Lua chunk as string or binary, and return a compiled chunk (`form'). .SS \f[B]\f[CB]luerl:loadfile(FileName[, CompileOptions], State) \-> {ok, Function, State} | CompileError\f[B]\f[R] Parse a Lua file, and return a compiled chunk (`form'). .SS \f[B]\f[CB]luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) \-> {ok,Function,FullName,State} | {error, Reason}\f[B]\f[R] Search Path until the file FileName is found. Parse the file and return a compiled chunk (`form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. .SS \f[B]\f[CB]luerl:load_module(KeyPath, ErlangModule, State) \-> State\f[B]\f[R] Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] which is \f[B]NOT\f[R] encoded. .SS \f[B]\f[CB]luerl:load_module_dec(EncodedKeyPath, ErlangModule, State) \-> State\f[B]\f[R] Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] which is encoded. .SS \f[B]\f[CB]luerl:do(String|Binary|Form, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError\f[B]\f[R] Evaluate a Lua expression and return its result which is \f[B]NOT\f[R] decoded, and the new Lua State. .SS \f[B]\f[CB]luerl:do_dec(String|Binary|Form, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError\f[B]\f[R] Evaluate a Lua expression and return its result which is automatically decoded, and the new Lua State. .SS \f[B]\f[CB]luerl:dofile(Path, State) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError\f[B]\f[R] Load and execute the Lua code in the file and return its result which is \f[B]NOT\f[R] decoded, and the new Lua State. Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). .SS \f[B]\f[CB]luerl:dofile_dec(Path[, State]) \-> {ok, Result, NewState} | {lua_error, Error, State} | CompileError\f[B]\f[R] Load and execute the Lua code in the file and return its result which is automatically decoded, and the new Lua State. .SS \f[B]\f[CB]luerl:call(FuncRef, ArgRefs, State) \-> {ok, Result, State}\f[B]\f[R] .SS \f[B]\f[CB]luerl:call_chunk(FuncRef, ArgRefs, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. .SS \f[B]\f[CB]luerl:call_function(FuncRef | FuncPath, ArgRefs, State] \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a function already defined in the state. \f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. .SS \f[B]\f[CB]luerl:call_function_enc(KeyPath, Args, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a function already defined in the state. \f[CR]KeyPath\f[R] is a list of keys to the function. \f[CR]KeyPath\f[R] and \f[CR]Args\f[R] are automatically encoded, while \f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. .SS \f[B]\f[CB]luerl:call_function_dec(KeyPath, Args, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a function already defined in the state. \f[CR]KeyPath\f[R] is a list of keys to the function. \f[CR]KeyPath\f[R] and \f[CR]Args\f[R] are automatically encoded and \f[CR]Result\f[R] is automatically decoded. .SS \f[B]\f[CB]luerl:call_method(ObjRef, Method, ArgRefs, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a method already defined in the state. \f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. .SS \f[B]\f[CB]luerl:call_method_enc(KeyPath, Method, Args, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a method already defined in the state. \f[CR]KeyPath\f[R] is a list of keys to the method. \f[CR]KeyPath\f[R], \f[CR]Method\f[R] and \f[CR]Args\f[R] are automatically encoded, while \f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. .SS \f[B]\f[CB]luerl:call_method_dec(KeyPath, Method, Args, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Call a method already defined in the state. \f[CR]KeyPath\f[R] is a list of keys to the method. \f[CR]KeyPath\f[R], \f[CR]Method\f[R] and \f[CR]Args\f[R] are automatically encoded and \f[CR]Result\f[R] is automatically decoded. .SS \f[B]\f[CB]luerl:get_table_keys(KeyPath, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Gets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] encoded/decoded. .SS \f[B]\f[CB]luerl:get_table_keys_dec(KeyPath, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Gets a value inside the Lua state. \f[CR]KeyPath\f[R] is automatically encoded and \f[CR]Result\f[R] is decoded. .SS \f[B]\f[CB]luerl:set_table_keys(KeyPath, Value, State) \-> {ok,State} | {lua_error, Error, State}\f[B]\f[R] Sets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. .SS \f[B]\f[CB]luerl:set_table_keys_dec(KeyPath, Value, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Sets a value inside the Lua state. \f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are automatically encoded and \f[CR]Result\f[R] is decoded. .SS \f[B]\f[CB]luerl:get_table_key(Table, Key, State) \-> {ok, Result, State} | {lua_error, Error, State}\f[B]\f[R] Gets the value of a key in a table. \f[CR]Table\f[R] and \f[CR]Key\f[R] are \f[B]NOT\f[R] encoded and \f[CR]Result\f[R] is \f[B]NOT\f[R] decoded. .SS \f[B]\f[CB]luerl:set_table_key(Table, Key, Value, State) \-> {ok, State} | {lua_error, Error, State}\f[B]\f[R] Sets the value of a key in a table. \f[CR]Table\f[R], \f[CR]Key\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. .SS \f[B]\f[CB]luerl:get_stacktrace(State) \-> [{FuncName,{file,FileName},{line,Line}}]\f[B]\f[R] Return a stack trace of the current call stack in the state. .SS \f[B]\f[CB]luerl:encode(Term, State) \-> {LuerlTerm,State}\f[B]\f[R] Encode the Erlang representation of a term into Luerl form updating the state when necessary. .SS \f[B]\f[CB]luerl:encode_list([Term], State) \-> {[LuerlTerm],State}\f[B]\f[R] Encode a list of Erlang term representations into a list of Luerl forms updating the state when necessary. .SS \f[B]\f[CB]luerl:decode(LuerlTerm, State) \-> Term\f[B]\f[R] Decode a term in the Luerl form into its Erlang representation. .SS \f[B]\f[CB]luerl:decode_list([LuerlTerm], State) \-> [Term]\f[B]\f[R] Decode a list of Luerl terms into a list of Erlang representations. .SS \f[B]\f[CB]luerl:put_private(Key, Term, State) \-> State.\f[B]\f[R] Puts a private value under key that is not exposed to the runtime. .SS \f[B]\f[CB]luerl:get_private(Key, State) \-> Term.\f[B]\f[R] Get a private value for the given key. .SS \f[B]\f[CB]luerl:delete_private(Key, State) \-> Term.\f[B]\f[R] Deletes the private value for the given key. .SS Passing String in the Erlang Shell Here we are going to look at passing in command strings into the \f[CR]luerl:do/2\f[R] function, especially when these strings contain Lua strings. The problem is to make sure that the strings in the Lua commands are processes correctly. .PP First just just doing it from the standard Lua shell as the source of truth: .IP .EX >return \[dq]aéb\[rs]235c\[dq] aéb?c >string.byte(\[dq]aéb\[rs]235c\[dq], 1, 20) 97 195 169 98 235 99 .EE .PP Now doing it from Erlang where I need to use \[rs]\[rs] to get a \[rs] into the string: .IP .EX 1> St = luerl:init(), ok. ok 2> f(S), S = \[dq]return \[aq]aéb\[rs]\[rs]235c\[aq]\[dq]. [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] 3> f(R), {ok,R,_} = luerl:do(S, St), io:write(R). [<<97,195,169,98,235,99>>] .EE .PP Now doing it from Elixir using the \f[CR]\[ti]C\f[R] sigil to get the handling of the string right: .IP .EX iex(1)> st = Luerl.init(); :ok :ok iex(4)> s = \[ti]C\[dq]return \[aq]aéb\[rs]235c\[aq]\[dq] [114,101,116,117,114,110,32,39,97,233,98,92,50,51,53,99,39] iex(3)> {ok,r,_} = Luerl.do(st, s); r [<<97, 195, 169, 98, 235, 99>>] .EE .PP So it works as expected. We use \f[CR]io:write\f[R] to write out the characters in the binary without any parsing or trying to be smart. .SH AUTHORS Jean Chassoul, Robert Virding.