PageRenderTime 17ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/luerl_lib_basic.erl

http://github.com/rvirding/luerl
Erlang | 399 lines | 280 code | 58 blank | 61 comment | 2 complexity | 878b744b6571eacc29d8a0292b884022 MD5 | raw file
Possible License(s): Apache-2.0
  1. %% Copyright (c) 2013-2020 Robert Virding
  2. %%
  3. %% Licensed under the Apache License, Version 2.0 (the "License");
  4. %% you may not use this file except in compliance with the License.
  5. %% You may obtain a copy of the License at
  6. %%
  7. %% http://www.apache.org/licenses/LICENSE-2.0
  8. %%
  9. %% Unless required by applicable law or agreed to in writing, software
  10. %% distributed under the License is distributed on an "AS IS" BASIS,
  11. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. %% See the License for the specific language governing permissions and
  13. %% limitations under the License.
  14. %% File : luerl_lib_basic.erl
  15. %% Author : Robert Virding
  16. %% Purpose : The basic library for Luerl.
  17. -module(luerl_lib_basic).
  18. -include("luerl.hrl").
  19. %% The basic entry point to set up the function table.
  20. -export([install/1]).
  21. %% Export some functions which can be called from elsewhere.
  22. -export([print/2,tostring/1,tostring/2]).
  23. -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten these
  24. install(St) ->
  25. luerl_heap:alloc_table(table(), St).
  26. %% table() -> [{FuncName,Function}].
  27. %% Caller will convert this list to the correct format.
  28. table() ->
  29. [{<<"_VERSION">>,<<"Lua 5.3">>}, %We are optimistic
  30. {<<"assert">>,#erl_func{code=fun assert/2}},
  31. {<<"collectgarbage">>,#erl_func{code=fun collectgarbage/2}},
  32. {<<"dofile">>,#erl_func{code=fun dofile/2}},
  33. {<<"eprint">>,#erl_func{code=fun eprint/2}},
  34. {<<"error">>,#erl_func{code=fun basic_error/2}},
  35. {<<"getmetatable">>,#erl_func{code=fun getmetatable/2}},
  36. {<<"ipairs">>,#erl_func{code=fun ipairs/2}},
  37. {<<"load">>,#erl_func{code=fun load/2}},
  38. {<<"loadfile">>,#erl_func{code=fun loadfile/2}},
  39. {<<"loadstring">>,#erl_func{code=fun loadstring/2}}, %For Lua 5.1 compatibility
  40. {<<"next">>,#erl_func{code=fun next/2}},
  41. {<<"pairs">>,#erl_func{code=fun pairs/2}},
  42. {<<"pcall">>,#erl_func{code=fun pcall/2}},
  43. {<<"print">>,#erl_func{code=fun print/2}},
  44. {<<"rawequal">>,#erl_func{code=fun rawequal/2}},
  45. {<<"rawget">>,#erl_func{code=fun rawget/2}},
  46. {<<"rawlen">>,#erl_func{code=fun rawlen/2}},
  47. {<<"rawset">>,#erl_func{code=fun rawset/2}},
  48. {<<"select">>,#erl_func{code=fun select/2}},
  49. {<<"setmetatable">>,#erl_func{code=fun setmetatable/2}},
  50. {<<"tonumber">>,#erl_func{code=fun tonumber/2}},
  51. {<<"tostring">>,#erl_func{code=fun tostring/2}},
  52. {<<"type">>,#erl_func{code=fun type/2}},
  53. {<<"unpack">>,#erl_func{code=fun unpack/2}} %For Lua 5.1 compatibility
  54. ].
  55. assert(As, St) ->
  56. case luerl_lib:boolean_value(As) of
  57. true -> {As,St};
  58. false ->
  59. M = case As of
  60. [_,M0|_] -> M0;
  61. _ -> <<"assertion failed">>
  62. end,
  63. lua_error({assert_error,M}, St)
  64. end.
  65. collectgarbage([], St) -> collectgarbage([<<"collect">>], St);
  66. collectgarbage([<<"collect">>|_], St) ->
  67. {[],luerl_heap:gc(St)};
  68. %% {[],St}; %No-op for the moment
  69. collectgarbage(_, St) -> %Ignore everything else
  70. {[],St}.
  71. eprint(Args, St) ->
  72. lists:foreach(fun (#tref{}=Tref) ->
  73. Tab = luerl_heap:get_table(Tref, St),
  74. io:format("~w ", [Tab]);
  75. (A) -> io:format("~w ", [A])
  76. end, Args),
  77. io:nl(),
  78. {[],St}.
  79. -spec basic_error(_, _) -> no_return().
  80. basic_error([{tref, _}=T|_], St0) ->
  81. case luerl_heap:get_metamethod(T, <<"__tostring">>, St0) of
  82. nil -> lua_error({error_call, T}, St0);
  83. Meta ->
  84. {[Ret|_], St1} = luerl_emul:functioncall(Meta, [T], St0),
  85. lua_error({error_call, Ret}, St1)
  86. end;
  87. basic_error([M|_], St) -> lua_error({error_call, M}, St); %Never returns!
  88. basic_error(As, St) -> badarg_error(error, As, St).
  89. %% ipairs(Args, State) -> {[Func,Table,FirstKey],State}.
  90. %% Return a function which on successive calls returns successive
  91. %% key-value pairs of integer keys.
  92. ipairs([#tref{}=Tref|_], St) ->
  93. case luerl_heap:get_metamethod(Tref, <<"__ipairs">>, St) of
  94. nil -> {[#erl_func{code=fun ipairs_next/2},Tref,0],St};
  95. Meta -> luerl_emul:functioncall(Meta, [Tref], St)
  96. end;
  97. ipairs(As, St) -> badarg_error(ipairs, As, St).
  98. ipairs_next([A], St) -> ipairs_next([A,0], St);
  99. ipairs_next([Tref,K|_], St) ->
  100. %% Get the table.
  101. #table{a=Arr} = luerl_heap:get_table(Tref, St),
  102. Next = K + 1,
  103. case array:get(Next, Arr) of
  104. nil -> {[nil],St};
  105. V -> {[Next,V],St}
  106. end.
  107. %% pairs(Args, State) -> {[Func,Table,Key],State}.
  108. %% Return a function to step over all the key-value pairs in a table.
  109. pairs([#tref{}=Tref|_], St) ->
  110. case luerl_heap:get_metamethod(Tref, <<"__pairs">>, St) of
  111. nil -> {[#erl_func{code=fun next/2},Tref,nil],St};
  112. Meta -> luerl_emul:functioncall(Meta, [Tref], St)
  113. end;
  114. pairs(As, St) -> badarg_error(pairs, As, St).
  115. %% next(Args, State) -> {[Key,Value] | [nil], State}.
  116. %% Given a table and a key return the next key-value pair in the
  117. %% table, or nil if there is no next key. The key 'nil' gives the
  118. %% first key-value pair.
  119. next([A], St) -> next([A,nil], St);
  120. next([#tref{}=Tref,K|_], St) ->
  121. %% Get the table.
  122. #table{a=Arr,d=Dict} = luerl_heap:get_table(Tref, St),
  123. if K == nil ->
  124. %% Find the first, start with the array.
  125. next_index(0, Arr, Dict, St);
  126. is_integer(K), K >= 1 ->
  127. next_index(K, Arr, Dict, St);
  128. is_float(K) ->
  129. case ?IS_FLOAT_INT(K, I) of
  130. true when I >= 1 ->
  131. next_index(I, Arr, Dict, St);
  132. _NegFalse -> %Not integer or negative
  133. next_key(K, Dict, St)
  134. end;
  135. true -> next_key(K, Dict, St)
  136. end;
  137. next(As, St) -> badarg_error(next, As, St).
  138. next_index(I0, Arr, Dict, St) ->
  139. case next_index_loop(I0+1, Arr, array:size(Arr)) of
  140. {I1,V} -> {[I1,V],St};
  141. none ->
  142. %% Nothing in the array, take table instead.
  143. first_key(Dict, St)
  144. end.
  145. next_index_loop(I, Arr, S) when I < S ->
  146. case array:get(I, Arr) of
  147. nil -> next_index_loop(I+1, Arr, S);
  148. V -> {I,V}
  149. end;
  150. next_index_loop(_, _, _) -> none.
  151. first_key(Dict, St) ->
  152. case ttdict:first(Dict) of
  153. {ok,{K,V}} -> {[K,V],St};
  154. error -> {[nil],St}
  155. end.
  156. next_key(K, Dict, St) ->
  157. case ttdict:next(K, Dict) of
  158. {ok,{N,V}} -> {[N,V],St};
  159. error -> {[nil],St}
  160. end.
  161. %% print(Args, State) -> {[],State}.
  162. %% Receives any number of arguments and prints their values to
  163. %% stdout, using the tostring function to convert each argument to a
  164. %% string. print is not intended for formatted output, but only as a
  165. %% quick way to show a value, for instance for debugging.
  166. print(Args, St0) ->
  167. St1 = lists:foldl(fun (A, S0) ->
  168. {[Str],S1} = tostring([A], S0),
  169. io:format("~ts ", [print_string(Str)]),
  170. S1
  171. end, St0, Args),
  172. io:nl(),
  173. {[],St1}.
  174. print_string(<<C/utf8,S/binary>>) -> [C|print_string(S)];
  175. print_string(<<_,S/binary>>) -> [$?|print_string(S)];
  176. print_string(<<>>) -> [].
  177. %% rawequal([Arg,Arg|_], State) -> {[Bool],State}.
  178. %% rawlen([Object|_], State) -> {[Length],State}.
  179. %% rawget([Table,Key|_], State) -> {[Val],State)}.
  180. %% rawset([Table,Key,Value|_]], State) -> {[Table],State)}.
  181. rawequal([A1,A2|_], St) -> {[A1 =:= A2],St};
  182. rawequal(As, St) -> badarg_error(rawequal, As, St).
  183. rawlen([A|_], St) when is_binary(A) -> {[float(byte_size(A))],St};
  184. rawlen([#tref{}=T|_], St) ->
  185. {[luerl_lib_table:raw_length(T, St)],St};
  186. rawlen(As, St) -> badarg_error(rawlen, As, St).
  187. rawget([#tref{}=Tref,Key|_], St) ->
  188. Val = luerl_heap:raw_get_table_key(Tref, Key, St),
  189. {[Val],St};
  190. rawget(As, St) -> badarg_error(rawget, As, St).
  191. rawset([Tref,nil=Key,_|_], St) ->
  192. lua_error({illegal_index,Tref,Key}, St);
  193. rawset([#tref{}=Tref,Key,Val|_], St0) ->
  194. St1 = luerl_heap:raw_set_table_key(Tref, Key, Val, St0),
  195. {[Tref],St1};
  196. rawset(As, St) -> badarg_error(rawset, As, St).
  197. %% select(Args, State) -> {[Element],State}.
  198. select([<<$#>>|As], St) -> {[float(length(As))],St};
  199. select([A|As], St) ->
  200. %%io:fwrite("sel:~p\n", [[A|As]]),
  201. Len = length(As),
  202. case luerl_lib:arg_to_integer(A) of
  203. N when is_integer(N), N > 0 -> {select_front(N, As, Len),St};
  204. N when is_integer(N), N < 0 -> {select_back(-N, As, Len),St};
  205. _ -> badarg_error(select, [A|As], St)
  206. end;
  207. select(As, St) -> badarg_error(select, As, St).
  208. select_front(N, As, Len) when N =< Len ->
  209. lists:nthtail(N-1, As);
  210. select_front(_, _, _) -> [].
  211. select_back(N, As, Len) when N =< Len ->
  212. lists:nthtail(Len-N, As);
  213. select_back(_, As, _) -> As.
  214. tonumber([Arg], St) -> {[tonumber(luerl_lib:arg_to_number(Arg))],St};
  215. tonumber([Arg,B|_], St) -> {[tonumber(luerl_lib:arg_to_number(Arg, B))],St};
  216. tonumber(As, St) -> badarg_error(tonumber, As, St).
  217. tonumber(Num) when is_number(Num) -> Num;
  218. tonumber(_) -> nil.
  219. tostring([Arg|_], St) ->
  220. case luerl_heap:get_metamethod(Arg, <<"__tostring">>, St) of
  221. nil -> {[tostring(Arg)],St};
  222. M when ?IS_FUNCTION(M) ->
  223. luerl_emul:functioncall(M, [Arg], St) %Return {R,St1}
  224. end.
  225. tostring(nil) -> <<"nil">>;
  226. tostring(false) -> <<"false">>;
  227. tostring(true) -> <<"true">>;
  228. tostring(N) when is_number(N) ->
  229. %% A = abs(N),
  230. %% %% Print really big/small "integers" as floats as well.
  231. %% S = if ?IS_FLOAT_INT(N), A < 1.0e14 ->
  232. %% integer_to_list(round(N));
  233. %% true -> io_lib:write(N)
  234. %% end,
  235. iolist_to_binary(io_lib:write(N));
  236. tostring(S) when is_binary(S) -> S;
  237. tostring(#tref{i=I}) ->
  238. iolist_to_binary([<<"table: ">>,integer_to_list(I)]);
  239. tostring(#usdref{i=I}) ->
  240. iolist_to_binary([<<"userdata: ">>,integer_to_list(I)]);
  241. tostring(#funref{i=I}) -> %Functions defined in Lua
  242. iolist_to_binary([<<"function: ">>,integer_to_list(I)]);
  243. tostring(#erl_func{code=C}) -> %Erlang functions
  244. iolist_to_binary([<<"function: ">>,io_lib:write(C)]);
  245. tostring(#thread{}) -> <<"thread">>;
  246. tostring(_) -> <<"unknown">>.
  247. type([Arg|_], St) -> {[type(Arg)],St}. %Only one return value!
  248. type(nil) -> <<"nil">>;
  249. type(N) when is_number(N) -> <<"number">>;
  250. type(S) when is_binary(S) -> <<"string">>;
  251. type(B) when is_boolean(B) -> <<"boolean">>;
  252. type(#tref{}) -> <<"table">>;
  253. type(#usdref{}) -> <<"userdata">>;
  254. type(#funref{}) -> <<"function">>; %Functions defined in Lua
  255. type(#erl_func{}) -> <<"function">>; %Internal functions
  256. type(#thread{}) -> <<"thread">>;
  257. type(_) -> <<"unknown">>.
  258. %% getmetatable([Value|_], State) -> {Table,State}.
  259. %% setmetatable([Table,Table|nil|_], State) -> {Table,State}.
  260. %% Can only set the metatable of tables here. Return tables for all
  261. %% values, for tables and userdata it is the table of the object,
  262. %% else the metatable for the type.
  263. getmetatable([Obj|_], St) ->
  264. case luerl_heap:get_metatable(Obj, St) of
  265. #tref{}=Meta ->
  266. #table{d=Dict} = luerl_heap:get_table(Meta, St),
  267. case ttdict:find(<<"__metatable">>, Dict) of
  268. {ok,MM} -> {[MM],St};
  269. error -> {[Meta],St}
  270. end;
  271. nil -> {[nil],St}
  272. end.
  273. setmetatable([#tref{}=T,#tref{}=M|_], St) ->
  274. do_setmetatable(T, M, St);
  275. setmetatable([#tref{}=T,nil|_], St) ->
  276. do_setmetatable(T, nil, St);
  277. setmetatable(As, St) -> badarg_error(setmetatable, As, St).
  278. do_setmetatable(#tref{}=Tref, Meta, St0) ->
  279. case luerl_heap:get_metamethod(Tref, <<"__metatable">>, St0) of
  280. nil ->
  281. Upd = fun (Tab) -> Tab#table{meta=Meta} end,
  282. St1 = luerl_heap:upd_table(Tref, Upd, St0),
  283. {[Tref],St1};
  284. _ -> badarg_error(setmetatable, [Tref], St0)
  285. end.
  286. %% Do files.
  287. dofile(As, St) ->
  288. case luerl_lib:conv_list(As, [erl_string]) of
  289. [File] ->
  290. Ret = luerl_comp:file(File), %Compile the file
  291. dofile_ret(Ret, As, St);
  292. _ -> badarg_error(dofile, As, St)
  293. end.
  294. dofile_ret({ok,Chunk}, _, St0) ->
  295. {Func,St1} = luerl_emul:load_chunk(Chunk, St0),
  296. luerl_emul:call(Func, [], St1);
  297. dofile_ret({error,_,_}, As, St) ->
  298. badarg_error(dofile, As, St).
  299. %% Load string and files.
  300. load(As, St) ->
  301. case luerl_lib:conv_list(As, [erl_string,lua_string,lua_string,lua_any]) of
  302. [S|_] ->
  303. Ret = luerl_comp:string(S), %Compile the string
  304. load_ret(Ret, St);
  305. error -> badarg_error(load, As, St)
  306. end.
  307. loadfile(As, St) ->
  308. case luerl_lib:conv_list(As, [erl_string,lua_string,lua_any]) of
  309. [F|_] ->
  310. Ret = luerl_comp:file(F), %Compile the file
  311. load_ret(Ret, St);
  312. error -> badarg_error(loadfile, As, St)
  313. end.
  314. loadstring(As, St) ->
  315. case luerl_lib:conv_list(As, [erl_string]) of
  316. [S] ->
  317. Ret = luerl_comp:string(S), %Compile the string
  318. load_ret(Ret, St);
  319. error -> badarg_error(loadstring, As, St)
  320. end.
  321. load_ret({ok,Chunk}, St0) ->
  322. {Func,St1} = luerl_emul:load_chunk(Chunk, St0),
  323. {[Func],St1};
  324. load_ret({error,[{_,Mod,E}|_],_}, St) ->
  325. Msg = iolist_to_binary(Mod:format_error(E)),
  326. {[nil,Msg],St}.
  327. pcall([F|As], St0) ->
  328. try
  329. {Rs,St1} = luerl_emul:functioncall(F, As, St0),
  330. {[true|Rs],St1}
  331. catch
  332. %% Only catch Lua errors here, signal system errors.
  333. error:{lua_error,{error_call, E},St2} ->
  334. {[false,E],St2};
  335. error:{lua_error,E,St2} ->
  336. %% Basic formatting for now.
  337. Msg = iolist_to_binary(luerl_lib:format_error(E)),
  338. {[false,Msg],St2}
  339. end.
  340. %% Lua 5.1 compatibility functions.
  341. unpack(As, St) -> luerl_lib_table:unpack(As, St).