Skip to content
Browse files

Merge branch 'develop'

  • Loading branch information...
2 parents e4a9e4e + e1de32d commit f782f3d65f387b0a4627f9750f71d7e064606d9f @rvirding committed
Showing with 435 additions and 328 deletions.
  1. +57 −62 README.md
  2. +17 −5 src/NOTES
  3. +16 −12 src/luerl.erl
  4. +43 −5 src/luerl.hrl
  5. +65 −85 src/luerl_basic.erl
  6. +93 −76 src/luerl_eval.erl
  7. +24 −7 src/luerl_lib.erl
  8. +120 −76 src/luerl_table.erl
View
119 README.md
@@ -113,7 +113,7 @@ Examples
#### separately parse, then execute
{ok, Chunk} = luerl:load("print(\"Hello, Chunk!\")"),
- State = luerl:start(),
+ State = luerl:init(),
{_Ret, _NewState} = luerl:do(Chunk, State),
@@ -154,67 +154,62 @@ Currently implemented functions in the libraries
- tonumber
- tostring
- type
-
-- io.flush
-- io.write
-
-- math.abs
-- math.acos
-- math.asin
-- math.atan
-- math.atan2
-- math.ceil
-- math.cos
-- math.cosh
-- math.deg
-- math.exp
-- math.floor
-- math.fmod
-- math.frexp
-- math.huge
-- math.ldexp
-- math.log
-- math.log10
-- math.max
-- math.min
-- math.modf
-- math.pi
-- math.pow
-- math.rad
-- math.random
-- math.randomseed
-- math.sin
-- math.sinh
-- math.sqrt
-- math.tan
-- math.tanh
-
-- os.clock
-- os.date
-- os.difftime
-- os.getenv
-- os.time
-
-- string.byte
-- string.char
-- string.find
-- string.format (very limited as yet)
-- string.gmatch
-- string.gsub
-- string.len
-- string.lower
-- string.match
-- string.rep
-- string.reverse
-- string.sub
-- string.upper
-
-- table.concat
-- table.insert
-- table.pack
-- table.remove
-- table.sort
-- table.unpack
+- io\.flush
+- io\.write
+- math\.abs
+- math\.acos
+- math\.asin
+- math\.atan
+- math\.atan2
+- math\.ceil
+- math\.cos
+- math\.cosh
+- math\.deg
+- math\.exp
+- math\.floor
+- math\.fmod
+- math\.frexp
+- math\.huge
+- math\.ldexp
+- math\.log
+- math\.log10
+- math\.max
+- math\.min
+- math\.modf
+- math\.pi
+- math\.pow
+- math\.rad
+- math\.random
+- math\.randomseed
+- math\.sin
+- math\.sinh
+- math\.sqrt
+- math\.tan
+- math\.tanh
+- os\.clock
+- os\.date
+- os\.difftime
+- os\.getenv
+- os\.time
+- string\.byte
+- string\.char
+- string\.find
+- string\.format (very limited as yet)
+- string\.gmatch
+- string\.gsub
+- string\.len
+- string\.lower
+- string\.match
+- string\.rep
+- string\.reverse
+- string\.sub
+- string\.upper
+- table\.concat
+- table\.insert
+- table\.pack
+- table\.remove
+- table\.sort
+- table\.unpack
Known Bugs
----------
View
22 src/NOTES
@@ -1,14 +1,26 @@
Implementation Notes
--------------------
-All tables are orddicts. We use this information when building/
-processing tables. Seems like Lua internaly stores elements as a
-(unordered) sequence of key-value elements.
+So far luerl is a straight interpreter and no compilation is
+done. This means that many simple optimisations are not done.
+
+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.
+
+All tables are combinations of orddicts and arrays. In each table an
+array is used for integer keys >= 1 while an orddict is used for all
+other keys. We use this information when building/ processing
+tables. Seems like Lua internaly stores elements as a (unordered)
+sequence of key-value elements, except maybe for the table part.
The table table can be either an ordict, an array or use the process
dictionary; 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 sue sets and
+inefficient. Either use bags and have one per table or use sets and
have the ETS key as {Tab,Key}.
To handle multiple return values we always return a list of values.
@@ -21,7 +33,7 @@ Similarily 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 predefinde libraries have an install/1 function. This is
+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.
View
28 src/luerl.erl
@@ -68,8 +68,8 @@ do(B, St) when is_binary(B) ->
do(S, St) when is_list(S) ->
{ok,C} = load(S),
luerl_eval:chunk(C, [], St);
-do(C, St) ->
- luerl_eval:call(C, [], St).
+do(C, St) -> %Pre-parsed/compiled chunk
+ luerl_eval:chunk(C, [], St).
%% luerl:dofile(Path[, State]) -> {Result, NewState}.
dofile(Path) ->
@@ -124,7 +124,7 @@ encode(I, St) when is_integer(I) -> {float(I),St};
encode(F, St) when is_float(F) -> {F,St};
encode(B, St) when is_boolean(B) -> {B,St};
encode(nil, St) -> {nil,St};
-encode(L, St0) ->
+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),
@@ -133,9 +133,9 @@ encode(L, St0) ->
{V1,S1} = encode(V0, S0),
{{I,V1},{I+1,S1}}
end, {1.0,St0}, L),
- Ts = orddict:from_list(Es),
- {T,St2} = luerl_eval:alloc_table(Ts, St1),
- {T,St2}.
+ {T,St2} = luerl_eval:alloc_table(Es, St1),
+ {T,St2}; %No more to do for now
+encode(_, _) -> error(badarg). %Can't encode anything else
%% decode_list([LuerlTerm], State) -> [Term].
%% decode(LuerlTerm, State) -> Term.
@@ -148,9 +148,13 @@ decode(N, _) when is_number(N) -> N;
decode(B, _) when is_boolean(B) -> B;
decode(nil, _) -> nil;
decode(#tref{i=N}, St) ->
- #table{a=Arr,t=Tab} = ?GET_TABLE(N, St#luerl.tabs),
- Fun = fun ({K,V}) -> {decode(K, St),decode(V, St)} end,
- At = lists:map(Fun, Arr),
- Tt = lists:map(Fun, Tab),
- Tt ++ At;
-decode({function,Fun}, _) -> {function,Fun}.
+ case ?GET_TABLE(N, St#luerl.tabs) of
+ #table{a=Arr,t=Tab} ->
+ Kfun = fun (K, V) -> {decode(K, St),decode(V, St)} end,
+ Afun = fun (K, V, Acc) -> [Kfun(K, V)|Acc] end,
+ [ Kfun(K, V) || {K,V} <- Tab ] ++
+ array:sparse_foldr(Afun, [], Arr);
+ _Undefined -> error(badarg)
+ end;
+decode({function,Fun}, _) -> {function,Fun};
+decode(_, _) -> error(badarg). %Shouldn't have anything else
View
48 src/luerl.hrl
@@ -60,10 +60,48 @@
-define(IS_INTEGER(N,I), (float(I=round(N)) =:= N)).
-define(IS_TRUE(X), (((X) =/= nil) and ((X) =/= false))).
+%% Different methods for storing the array section of a table in
+%% #table{}. Solely using these macros allows testing with different
+%% storage methods. When using orddicts it is more efficient to KNOW
+%% the defined internal structure.
+
+%% Set which array store to use.
+-define(ARR_USE_ARRAY, true).
+
+-ifdef(ARR_USE_ARRAY).
+-define(MAKE_ARR(), array:new([{default,nil}])).
+-define(ASIZE(A), array:size(A)).
+-define(AGET(I, A), array:get(I, A)).
+-define(ASET(I, V, A), array:set(I, V, A)).
+-endif.
+
+-ifdef(ARR_USE_ORRDICT).
+-define(MAKE_ARR(), orddict:new()).
+-define(ASIZE(A), orddict:size(A)). %This not correct!
+-define(AGET(I, A), case orddict:find(I, A) of
+ {ok,___V} -> ___V;
+ error -> nil
+ end).
+-define(ASET(I, V, A), orddict:store(I, V, A)).
+-endif.
+
+-ifdef(ARR_USE_EXP).
+%% Use experimental array structure in luerl_lib.
+-define(MAKE_ARR(), luerl_lib:anew()).
+-define(ASIZE(A), luerl_lib:asiz(A)).
+-define(AGET(I, A), luerl_lib:aget(I, A)).
+-define(ASET(I, V, A), luerl_lib:aset(I, V, A)).
+-endif.
+
+%% 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 acces with match/select.
+
%% Set which table store to use.
--define(USE_ARRAY, true).
+-define(TS_USE_ARRAY, true).
--ifdef(USE_ORDDICT).
+-ifdef(TS_USE_ORDDICT).
%% Using orddict to handle tables.
-define(MAKE_TABLE(), orddict:new()).
-define(GET_TABLE(N, Ts), orddict:fetch(N, Ts)).
@@ -74,7 +112,7 @@
-define(FOLD_TABLES(Fun, Acc, Ts), orddict:fold(Fun, Acc, Ts)).
-endif.
--ifdef(USE_ARRAY).
+-ifdef(TS_USE_ARRAY).
%% Use arrays to handle tables.
-define(MAKE_TABLE(), array:new()).
-define(GET_TABLE(N, Ar), array:get(N, Ar)).
@@ -95,7 +133,7 @@
-define(FOLD_TABLES(Fun, Acc, Ar), array:sparse_foldl(Fun, Acc, Ar)).
-endif.
--ifdef(USE_PD).
+-ifdef(TS_USE_PD).
%% Use the process dictionary to handle tables.
-define(MAKE_TABLE(), ok).
-define(GET_TABLE(N, Pd), get(N)).
@@ -106,7 +144,7 @@
-define(FOLD_TABLES(Fun, Acc, Pd), Pd). %This needs work
-endif.
--ifdef(USE_ETS).
+-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)).
View
150 src/luerl_basic.erl
@@ -33,7 +33,7 @@
-export([install/1]).
--import(luerl_lib, [lua_error/1]). %Shorten this
+-import(luerl_lib, [lua_error/1,badarg_error/2]). %Shorten these
install(St) ->
luerl_eval:alloc_table(table(), St).
@@ -68,7 +68,7 @@ table() ->
].
assert(As, St) ->
- case luerl_lib:is_true(As) of
+ case luerl_lib:is_true_value(As) of
true -> {As,St};
false ->
M = case As of
@@ -95,30 +95,28 @@ eprint(Args, St) ->
{[],St}.
error([M|_], _) -> lua_error(M); %Never returns!
-error(As, _) -> lua_error({badarg,error,As}).
+error(As, _) -> badarg_error(error, As).
ipairs([#tref{}=Tref|_], St) ->
case luerl_eval:getmetamethod(Tref, <<"__ipairs">>, St) of
nil -> {[{function,fun ipairs_next/2},Tref,0.0],St};
Meta -> luerl_eval:functioncall(Meta, [Tref], St)
end;
-ipairs(As, _) -> lua_error({badarg,ipairs,As}).
+ipairs(As, _) -> badarg_error(ipairs, As).
ipairs_next([A], St) -> ipairs_next([A,0.0], St);
ipairs_next([#tref{i=T},K|_], St) ->
#table{a=Arr} = ?GET_TABLE(T, St#luerl.tabs), %Get the table
case ?IS_INTEGER(K, I) of
- %% true ->
true when I >= 0 ->
Next = I + 1,
- case orddict:find(Next, Arr) of
- {ok,V} when V =/= nil -> %Only non-nil values
- {[float(Next),V],St};
- _ -> {[nil],St} %No more or nil
+ case raw_get_index(Arr, Next) of
+ nil -> {[nil],St};
+ V -> {[float(Next),V],St}
end;
_NegFalse -> lua_error({invalid_key,ipairs,K})
end;
-ipairs_next(As, _) -> lua_error({badarg,ipairs,As}).
+ipairs_next(As, _) -> badarg_error(ipairs, As).
next([A], St) -> next([A,nil], St);
next([#tref{i=T},K|_], St) ->
@@ -126,51 +124,36 @@ next([#tref{i=T},K|_], St) ->
if K == nil ->
%% Find the first, start with the array.
%% io:format("n: ~p\n", [{Arr,Tab}]),
- case first_index(Arr) of
- {I,V} -> {[float(I),V],St};
- none ->
- %% Nothing in the array, take table
- case Tab of
- [{F,V}|_] -> {[F,V],St};
- [] -> {[nil],St}
- end
- end;
+ next_index(0, Arr, Tab, St);
is_number(K) ->
case ?IS_INTEGER(K, I0) of
- %% true ->
true when I0 >= 1 ->
- case next_index(I0, Arr) of
- {I1,V} -> {[float(I1),V],St};
- none ->
- %% None left in array, take table.
- case Tab of
- [{F,V}|_] -> {[F,V],St};
- [] -> {[nil],St}
- end
- end;
+ next_index(I0, Arr, Tab, St);
_NegFalse -> next_key(K, Tab, St) %Not integer or negative
end;
true -> next_key(K, Tab, St)
end;
-next(As, _) -> lua_error({badarg,next,As}).
-
-first_index([E|_]) -> E;
-first_index([]) -> none.
-
-next_index(I, Arr) ->
- case next_index_loop(I, Arr) of
- [{I1,V}|_] -> {I1,V};
- _ -> none
+next(As, _) -> badarg_error(next, As).
+
+next_index(I0, Arr, Tab, St) ->
+ case next_index_loop(I0+1, Arr, array:size(Arr)) of
+ {I1,V} -> {[float(I1),V],St};
+ none ->
+ %% Nothing in the array, take table instead.
+ {first_key(Tab),St}
end.
-next_index_loop(I, [{I,_}|Arr]) -> Arr; %The next one
-next_index_loop(I, [{K,_}|_]=Arr) when K > I -> Arr;
-next_index_loop(I, [{K,_}|Arr]) when K < I -> %Not there yet
- next_index_loop(I, Arr);
-next_index_loop(_, []) -> none. %Nothing there
+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([{K,V}|_]) -> [K,V];
+first_key([]) -> [nil].
next_key(K, Tab, St) ->
- %% io:fwrite("nk: ~p\n", [{K,lists:sublist(Tab,20)}]),
case next_key_loop(K, Tab) of
[{Next,V}|_] -> {[Next,V],St};
[] -> {[nil],St};
@@ -189,7 +172,7 @@ pairs([#tref{}=Tref|_], St) ->
nil -> {[{function,fun next/2},Tref,nil],St};
Meta -> luerl_eval:functioncall(Meta, [Tref], St)
end;
-pairs(As, _) -> lua_error({badarg,pairs,As}).
+pairs(As, _) -> badarg_error(pairs, As).
print(Args, St0) ->
St1 = lists:foldl(fun (A, S0) ->
@@ -201,51 +184,32 @@ print(Args, St0) ->
{[],St1}.
rawequal([A1,A2|_], St) -> {[A1 =:= A2],St};
-rawequal(As, _) -> lua_error({badarg,rawequal,As}).
+rawequal(As, _) -> badarg_error(rawequal, As).
rawget([#tref{i=N},K|_], St) when is_number(K) ->
#table{a=Arr,t=Tab} = ?GET_TABLE(N, St#luerl.tabs), %Get the table.
- case ?IS_INTEGER(K, I) of
- %% true -> %Array index
- true when I >= 1 -> %Array index
- case orddict:find(I, Arr) of
- {ok,V} -> {[V],St};
- error -> {[nil],St}
- end;
- _NegFalse -> %Negative or false
- case orddict:find(K, Tab) of
- {ok,V} -> {[V],St};
- error -> {[nil],St}
- end
- end;
+ V = case ?IS_INTEGER(K, I) of
+ true when I >= 1 -> %Array index
+ raw_get_index(Arr, I);
+ _NegFalse -> %Negative or false
+ raw_get_key(Tab, K)
+ end,
+ {[V],St};
rawget([#tref{i=N},K|_], St) ->
#table{t=Tab} = ?GET_TABLE(N, St#luerl.tabs), %Get the table.
- case orddict:find(K, Tab) of
- {ok,V} -> {[V],St};
- error -> {[nil],St}
- end;
-rawget(As, _) -> lua_error({badarg,rawget,As}).
-
-raw_get_index(Arr, I) -> orddict:find(I, Arr).
-
-raw_get_key(I, Tab) -> orddict:find(I, Tab).
-
-raw_set_index(Arr, I, nil) -> orddict:erase(I, Arr);
-raw_set_index(Arr, I, V) -> orddict:store(I, V, Arr).
-
-raw_set_key(Arr, I, nil) -> orddict:erase(I, Arr);
-raw_set_key(Arr, I, V) -> orddict:store(I, V, Arr).
+ V = raw_get_key(Tab, K),
+ {[V],St};
+rawget(As, _) -> badarg_error(rawget, As).
rawlen([A|_], St) when is_binary(A) -> {[float(byte_size(A))],St};
rawlen([#tref{i=N}|_], St) ->
#table{a=Arr} = ?GET_TABLE(N, St#luerl.tabs),
- {[length(Arr)],St};
-rawlen(As, _) -> lua_error({badarg,rawlen,As}).
+ {[float(array:size(Arr))],St};
+rawlen(As, _) -> badarg_error(rawlen, As).
rawset([#tref{i=N}=Tref,K,V|_], #luerl{tabs=Ts0}=St) when is_number(K) ->
#table{a=Arr0,t=Tab0}=T = ?GET_TABLE(N, Ts0),
Ts1 = case ?IS_INTEGER(K, I) of
- %% true ->
true when I >= 1 ->
Arr1 = raw_set_index(Arr0, I, V),
?SET_TABLE(N, T#table{a=Arr1}, Ts0);
@@ -259,7 +223,23 @@ rawset([#tref{i=N}=Tref,K,V|_], #luerl{tabs=Ts0}=St) ->
Tab1 = raw_set_key(Tab0, K, V),
Ts1 = ?SET_TABLE(N, T#table{t=Tab1}, Ts0),
{[Tref],St#luerl{tabs=Ts1}};
-rawset(As, _) -> lua_error({badarg,rawset,As}).
+rawset(As, _) -> badarg_error(rawset, As).
+
+%% raw_get_index(Array, Index) -> nil | Value.
+%% raw_get_key(Table, Key) -> nil | Value.
+
+raw_get_index(Arr, I) -> array:get(I, Arr).
+
+raw_get_key(Tab, K) ->
+ case orddict:find(K, Tab) of
+ {ok,V} -> V;
+ error -> nil
+ end.
+
+raw_set_index(Arr, I, V) -> array:set(I, V, Arr).
+
+raw_set_key(Tab, K, nil) -> orddict:erase(K, Tab);
+raw_set_key(Tab, K, V) -> orddict:store(K, V, Tab).
select([<<$#>>|As], St) -> {[float(length(As))],St};
select([A|As], St) ->
@@ -268,9 +248,9 @@ select([A|As], St) ->
case luerl_lib:to_int(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};
- _ -> lua_error({badarg,select,[A|As]})
+ _ -> badarg_error(select, [A|As])
end;
-select(As, _) -> lua_error({badarg,select,As}).
+select(As, _) -> badarg_error(select, As).
select_front(N, As, Len) when N =< Len ->
lists:nthtail(N-1, As);
@@ -282,7 +262,7 @@ select_back(_, As, _) -> As.
tonumber([Arg], St) -> {[luerl_lib:tonumber(Arg)],St};
tonumber([Arg,B|_], St) -> {[luerl_lib:tonumber(Arg, B)],St};
-tonumber(As, _) -> lua_error({badarg,tonumber,As}).
+tonumber(As, _) -> badarg_error(tonumber, As).
tostring([Arg|_], St) ->
case luerl_eval:getmetamethod(Arg, <<"__tostring">>, St) of
@@ -344,14 +324,14 @@ setmetatable([#tref{i=N}=A1,#tref{}=A2|_], St) ->
setmetatable([#tref{i=N}=A1,nil|_], St) ->
Ts = ?UPD_TABLE(N, fun (Tab) -> Tab#table{m=nil} end, St#luerl.tabs),
{[A1],St#luerl{tabs=Ts}};
-setmetatable(As, _) -> lua_error({badarg,setmetatable,As}).
+setmetatable(As, _) -> badarg_error(setmetatable, As).
%% Load string and files.
load(As, St) ->
case luerl_lib:conv_list(As, [string]) of
[S] -> do_load(S, St);
- nil -> lua_error({badarg,load,As})
+ nil -> badarg_error(load, As)
end.
loadfile(As, St) ->
@@ -363,7 +343,7 @@ loadfile(As, St) ->
Msg = iolist_to_binary(file:format_error(E)),
{[nil,Msg],St}
end;
- nil -> lua_error({badarg,loadfile,As})
+ nil -> badarg_error(loadfile, As)
end.
do_load(S, St) ->
@@ -390,7 +370,7 @@ dofile(As, St) ->
{ok,Bin} = file:read_file(File),
{ok,C} = parse_string(binary_to_list(Bin)),
luerl_eval:chunk(C, St);
- _ -> lua_error({badarg,dofile,As})
+ _ -> badarg_error(dofile, As)
end.
parse_string(S) ->
View
169 src/luerl_eval.erl
@@ -35,7 +35,7 @@
-include("luerl.hrl").
%% Basic interface.
--export([init/0,chunk/2,chunk/3,funchunk/2,funchunk/3,gc/1]).
+-export([init/0,call/2,call/3,chunk/2,chunk/3,funchunk/2,funchunk/3,gc/1]).
%% Internal functions which can be useful "outside".
-export([alloc_table/2,functioncall/3,get_table_key/3,
@@ -45,10 +45,10 @@
-export([alloc_table/1,set_local_keys/3,set_local_keys_tab/3,
get_local_key/2,set_env_name_env/4]).
--import(luerl_lib, [lua_error/1]). %Shorten this
+-import(luerl_lib, [lua_error/1,badarg_error/2]).
%% -compile(inline). %For when we are optimising
-%% -compile({inline,[is_true/1,first_value/1]}).
+%% -compile({inline,[is_true_value/1,first_value/1]}).
%%-define(DP(F,As), io:format(F, As)).
-define(DP(F, A), ok).
@@ -97,8 +97,10 @@ pop_env(#luerl{env=[_|Es]}=St) ->
%% alloc_table(State) -> {Tref,State}.
%% alloc_table(InitialTable, State) -> {Tref,State}.
%% free_table(Tref, State) -> State.
+%% The InitialTable is [{Key,Value}], there is no longer any need to
+%% have it as an orddict.
-alloc_table(St) -> alloc_table(orddict:new(), St).
+alloc_table(St) -> alloc_table([], St).
alloc_table(Itab, #luerl{tabs=Ts0,free=[N|Ns]}=St) ->
T = init_table(Itab),
@@ -113,12 +115,11 @@ alloc_table(Itab, #luerl{tabs=Ts0,free=[],next=N}=St) ->
init_table(Itab) ->
T0 = orddict:new(),
- A0 = orddict:new(), %These are still orddicts!
+ A0 = array:new([{default,nil}]), %Arrays with 'nil' as default
Init = fun ({_,nil}, {T,A}) -> {T,A}; %Ignore nil values
({K,V}, {T,A}) when is_number(K) ->
case ?IS_INTEGER(K, I) of
- %% true -> {T,orddict:store(I, V, A)};
- true when I >= 1 -> {T,orddict:store(I, V, A)};
+ true when I >= 1 -> {T,array:set(I, V, A)};
_NegFalse -> {orddict:store(K, V, T),A}
end;
({K,V}, {T,A}) -> {orddict:store(K, V, T),A}
@@ -146,7 +147,6 @@ set_table_name(Tab, Name, Val, St) ->
set_table_key(#tref{}=Tref, Key, Val, St) when is_number(Key) ->
case ?IS_INTEGER(Key, I) of
- %% true -> set_table_int_key(Tref, Key, I, Val, St);
true when I >= 1 -> set_table_int_key(Tref, Key, I, Val, St);
_NegFalse -> set_table_key_key(Tref, Key, Val, St)
end;
@@ -180,29 +180,25 @@ set_table_key_key(#tref{i=N}, Key, Val, #luerl{tabs=Ts0}=St) ->
set_table_int_key(#tref{i=N}, Key, I, Val, #luerl{tabs=Ts0}=St) ->
#table{a=Arr0,m=Meta}=T = ?GET_TABLE(N, Ts0), %Get the table
- case orddict:find(I, Arr0) of
- {ok,_} -> %Key exists
- %% Should we do this here?
- %% Arr1 = orddict:store(I, Val, Arr0),
- Arr1 = if Val =:= nil -> orddict:erase(Key, Arr0);
- true -> orddict:store(Key, Val, Arr0)
- end,
- Ts1 = ?SET_TABLE(N, T#table{a=Arr1}, Ts0),
- St#luerl{tabs=Ts1};
- error -> %Key does not exist
+ case array:get(I, Arr0) of
+ nil -> %Key does not exist
case getmetamethod_tab(Meta, <<"__newindex">>, Ts0) of
nil ->
- %% Only add non-nil value.
+ %% Only add non-nil value, slightly faster (?)
Arr1 = if Val =:= nil -> Arr0;
- true -> orddict:store(I, Val, Arr0)
+ true -> array:set(I, Val, Arr0)
end,
- %%Arr1 = orddict:store(I, Val, Arr0),
Ts1 = ?SET_TABLE(N, T#table{a=Arr1}, Ts0),
St#luerl{tabs=Ts1};
Meth when element(1, Meth) =:= function ->
functioncall(Meth, [Key,Val], St);
Meth -> set_table_key(Meth, Key, Val, St)
- end
+ 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),
+ St#luerl{tabs=Ts1}
end.
get_table_name(Tab, Name, St) ->
@@ -210,7 +206,6 @@ get_table_name(Tab, Name, St) ->
get_table_key(#tref{}=Tref, Key, St) when is_number(Key) ->
case ?IS_INTEGER(Key, I) of
- %% true -> get_table_int_key(Tref, Key, I, St);
true when I >= 1 -> get_table_int_key(Tref, Key, I, St);
_NegFalse -> get_table_key_key(Tref, Key, St)
end;
@@ -237,11 +232,11 @@ get_table_key_key(#tref{i=N}=T, Key, #luerl{tabs=Ts}=St) ->
get_table_int_key(#tref{i=N}=T, Key, I, #luerl{tabs=Ts}=St) ->
#table{a=A,m=Meta} = ?GET_TABLE(N, Ts), %Get the table.
- case orddict:find(I, A) of
- {ok,Val} -> {[Val],St};
- error ->
+ case array:get(I, A) of
+ nil ->
%% Key not present so try metamethod
- get_table_metamethod(T, Meta, Key, Ts, St)
+ get_table_metamethod(T, Meta, Key, Ts, St);
+ Val -> {[Val],St}
end.
get_table_metamethod(T, Meta, Key, Ts, St) ->
@@ -349,26 +344,30 @@ get_env_key_env(K, Ts, [#tref{i=E}|Es]) ->
end;
get_env_key_env(_, _, []) -> nil. %The default value
-%% chunk(Stats, State) -> {Return,State}.
+%% chunk(Chunk, State) -> {Return,State}.
+%% chunk(Chunk, Args, State) -> {Return,State}.
+
+chunk(Chunk, St) -> chunk(Chunk, [], St).
+chunk(Chunk, Args, St) -> call(Chunk, Args, St).
-chunk(Stats, St0) ->
- {Ret,St1} = function_block(fun (S) -> {[],stats(Stats, S)} end, St0),
- %% Should do GC here.
- {Ret,St1}.
+%% call(Chunk, State) -> {Return,State}.
+%% call(Chunk, Args, State) -> {Return,State}.
-%% chunk(Chunk, Args, State) -> {Return,State}.
+call(Chunk, St) -> call(Chunk, [], St).
-chunk({functiondef,L,_,Ps,B}, Args, St0) ->
- {[Lf],St1} = exp({functiondef,L,Ps,B}, St0),
- {Ret,St2} = functioncall(Lf, Args, St1),
+call({functiondef,L,_,Ps,B}, Args, St0) ->
+ %% Generate function and call it.
+ {[Func],St1} = exp({functiondef,L,Ps,B}, St0),
+ {Ret,St2} = functioncall(Func, Args, St1),
%% Should do GC here.
{Ret,St2};
-chunk({functiondef,L,Ps,B}, Args, St0) ->
- {[Lf],St1} = exp({functiondef,L,Ps,B}, St0),
- {Ret,St2} = functioncall(Lf, Args, St1),
+call({functiondef,L,Ps,B}, Args, St0) ->
+ %% Generate function and call it.
+ {[Func],St1} = exp({functiondef,L,Ps,B}, St0),
+ {Ret,St2} = functioncall(Func, Args, St1),
%% Should do GC here.
{Ret,St2};
-chunk({function,_}=Func, Args, St0) ->
+call({function,_}=Func, Args, St0) ->
{Ret,St1} = functioncall(Func, Args, St0),
%% Should do GC here.
{Ret,St1}.
@@ -487,8 +486,8 @@ assign_loop([], _, St) -> St.
set_var({'.',_,Exp,Rest}, Val, St0) ->
{[Next|_],St1} = prefixexp_first(Exp, St0),
var_rest(Rest, Val, Next, St1);
-set_var({'NAME',_,Name}, Val, St) ->
- set_env_name(Name, Val, St).
+set_var({'NAME',_,N}, Val, St) ->
+ set_env_name(N, Val, St).
var_rest({'.',_,Exp,Rest}, Val, SoFar, St0) ->
{[Next|_],St1} = prefixexp_element(Exp, SoFar, St0),
@@ -514,7 +513,7 @@ do_while(Exp, Body, St0) ->
while_loop(Exp, Body, St0) ->
{Test,St1} = exp(Exp, St0),
- case is_true(Test) of
+ case is_true_value(Test) of
true ->
St2 = block(Body, St1),
while_loop(Exp, Body, St2);
@@ -534,7 +533,7 @@ do_repeat(Body, Exp, St0) ->
repeat_loop(Body, St0) ->
{Ret,St1} = with_block(Body, St0),
- case is_true(Ret) of
+ case is_true_value(Ret) of
true -> {[],St1};
false -> repeat_loop(Body, St1)
end.
@@ -571,8 +570,8 @@ do_if(Tests, Else, St) ->
if_tests([{Exp,Block}|Ts], Else, St0) ->
{Test,St1} = exp(Exp, St0), %What about the environment
- case is_true(Test) of
- true -> %Test succeeded, do block
+ case is_true_value(Test) of
+ true -> %Test succeeded, do block
block(Block, St1);
false -> %Test failed, try again
if_tests(Ts, Else, St1)
@@ -581,11 +580,11 @@ if_tests([], Else, St) -> block(Else, St).
%% do_numfor(Line, Var, Init, Limit, Step, Block, State) -> State.
-do_numfor(_, {'NAME',_,Name}, Init, Limit, Step, Block, St0) ->
- NumFor = fun (St) -> numeric_for(Name, Init, Limit, Step, Block, St) end,
- %% io:fwrite("dn: ~p\n", [{Name,St0#luerl.locf}]),
+do_numfor(_, {'NAME',_,N}, Init, Limit, Step, Block, St0) ->
+ NumFor = fun (St) -> numeric_for(N, Init, Limit, Step, Block, St) end,
+ %% io:fwrite("dn: ~p\n", [{N,St0#luerl.locf}]),
{_,St1} = loop_block(NumFor, St0),
- %% io:fwrite("dn->~p\n", [{Name,St1#luerl.locf}]),
+ %% io:fwrite("dn->~p\n", [{N,St1#luerl.locf}]),
St1.
numeric_for(Name, Init, Limit, Step, Block, St) ->
@@ -635,7 +634,7 @@ generic_for(Names, Exps, Block, St) ->
genfor_loop(Names, F, S, Var, Block, St0) ->
{Vals,St1} = functioncall(F, [S,Var], St0),
- case is_true(Vals) of
+ case is_true_value(Vals) of
true -> %We go on
%% Create a local block for each iteration of the loop.
Do = fun (S0) ->
@@ -648,10 +647,10 @@ genfor_loop(Names, F, S, Var, Block, St0) ->
false -> {[],St1} %Done
end.
-local({functiondef,L,{'NAME',_,Name},Ps,B}, #luerl{tabs=Ts0,env=Env}=St) ->
+local({functiondef,L,{'NAME',_,N},Ps,B}, #luerl{tabs=Ts0,env=Env}=St) ->
%% Set name separately first so recursive call finds right Name.
- Ts1 = set_local_name_env(Name, nil, Ts0, Env),
- Ts2 = set_local_name_env(Name, {function,L,Env,Ps,B}, Ts1, Env),
+ Ts1 = set_local_name_env(N, nil, Ts0, Env),
+ Ts2 = set_local_name_env(N, {function,L,Env,Ps,B}, Ts1, Env),
St#luerl{tabs=Ts2,locf=true};
local({assign,_,Ns,Es}, St0) ->
{Vals,St1} = explist(Es, St0),
@@ -704,7 +703,7 @@ exp({table,_,Fs}, St0) ->
%% 'and' and 'or' short-circuit so need special handling.
exp({op,_,'and',L0,R0}, St0) ->
{L1,St1} = exp(L0, St0),
- case is_true(L1) of
+ case is_true_value(L1) of
true ->
{R1,St2} = exp(R0, St1),
{R1,St2}; %Do we need first value?
@@ -712,7 +711,7 @@ exp({op,_,'and',L0,R0}, St0) ->
end;
exp({op,_,'or',L0,R0}, St0) ->
{L1,St1} = exp(L0, St0),
- case is_true(L1) of
+ case is_true_value(L1) of
true -> {L1,St1};
false ->
{R1,St2} = exp(R0, St1),
@@ -730,8 +729,16 @@ exp(E, St) ->
prefixexp(E, St).
%% prefixexp(PrefixExp, State) -> {[Vals],State}.
-%% Step down the prefixexp sequence evaluating as we go.
+%% Step down the prefixexp sequence evaluating as we go. We special
+%% checking for functions/methods to give better errors, after an idea
+%% by @slepher.
+prefixexp({'.',_,{'NAME',_,N}=Exp,{functioncall,_,_}=Rest}, St0) ->
+ {Next,St1} = prefixexp_first(Exp, St0),
+ case first_value(Next) of
+ nil -> lua_error({undef_function,N});
+ Fval -> prefixexp_rest(Rest, Fval, St1)
+ end;
prefixexp({'.',_,Exp,Rest}, St0) ->
{Next,St1} = prefixexp_first(Exp, St0),
prefixexp_rest(Rest, first_value(Next), St1);
@@ -741,8 +748,15 @@ prefixexp_first({'NAME',_,N}, St) -> {[get_env_name(N, St)],St};
prefixexp_first({single,_,E}, St0) -> %Guaranteed only one value
%% io:format("pf: ~p\n", [E]),
{R,St1} = exp(E, St0),
- {[first_value(R)],St1}.
+ {[first_value(R)],St1}. %Only one value!
+prefixexp_rest({'.',_,{'NAME',_,N}=Exp,{functioncall,_,_}=Rest},
+ SoFar, St0) ->
+ {Next,St1} = prefixexp_element(Exp, SoFar, St0),
+ case first_value(Next) of
+ nil -> lua_error({undef_function,N});
+ Fval -> prefixexp_rest(Rest, Fval, St1)
+ end;
prefixexp_rest({'.',_,Exp,Rest}, SoFar, St0) ->
{Next,St1} = prefixexp_element(Exp, SoFar, St0),
prefixexp_rest(Rest, first_value(Next), St1);
@@ -762,9 +776,13 @@ prefixexp_element({key_field,_,Exp}, SoFar, St0) ->
{V,St2};
prefixexp_element({method,_,{'NAME',_,N},Args0}, SoFar, St0) ->
{Func,St1} = get_table_name(SoFar, N, St0),
- {Args1,St2} = explist(Args0, St1),
- %%io:fwrite("pe2: ~p\n", [{Func,[SoFar|Args1]}]),
- functioncall(first_value(Func), [SoFar|Args1], St2).
+ case first_value(Func) of
+ nil -> lua_error({undef_function,N});
+ Fval ->
+ {Args1,St2} = explist(Args0, St1),
+ %%io:fwrite("pe2: ~p\n", [{Func,[SoFar|Args1]}]),
+ functioncall(Fval, [SoFar|Args1], St2)
+ end.
functioncall({function,_,Env,Ps,B}, Args, St0) ->
Env0 = St0#luerl.env, %Caller's environment
@@ -941,7 +959,7 @@ numeric_op(_Op, O, E, Raw, St0) ->
true ->
Meta = getmetamethod(O, E, St0),
{Ret,St1} = functioncall(Meta, [O], St0),
- {[is_true(Ret)],St1}
+ {[is_true_value(Ret)],St1}
end.
numeric_op(_Op, O1, O2, E, Raw, St0) ->
@@ -951,18 +969,18 @@ numeric_op(_Op, O1, O2, E, Raw, St0) ->
true ->
Meta = getmetamethod(O1, O2, E, St0),
{Ret,St1} = functioncall(Meta, [O1,O2], St0),
- {[is_true(Ret)],St1}
+ {[is_true_value(Ret)],St1}
end.
eq_op(_Op, O1, O2, St) when O1 =:= O2 -> {[true],St};
eq_op(_Op, O1, O2, St0) ->
{Ret,St1} = eq_meta(O1, O2, St0),
- {[is_true(Ret)],St1}.
+ {[is_true_value(Ret)],St1}.
neq_op(_Op, O1, O2, St) when O1 =:= O2 -> {[false],St};
neq_op(_Op, O1, O2, St0) ->
{Ret,St1} = eq_meta(O1, O2, St0),
- {[not is_true(Ret)],St1}.
+ {[not is_true_value(Ret)],St1}.
eq_meta(O1, O2, St0) ->
case getmetamethod(O1, <<"__eq">>, St0) of
@@ -980,7 +998,7 @@ lt_op(_Op, O1, O2, St) when is_binary(O1), is_binary(O2) -> {[O1 < O2],St};
lt_op(_Op, O1, O2, St0) ->
Meta = getmetamethod(O1, O2, <<"__lt">>, St0),
{Ret,St1} = functioncall(Meta, [O1,O2], St0),
- {[is_true(Ret)],St1}.
+ {[is_true_value(Ret)],St1}.
le_op(_Op, O1, O2, St) when is_number(O1), is_number(O2) -> {[O1 =< O2],St};
le_op(_Op, O1, O2, St) when is_binary(O1), is_binary(O2) -> {[O1 =< O2],St};
@@ -988,12 +1006,12 @@ le_op(_Op, O1, O2, St0) ->
case getmetamethod(O1, O2, <<"__le">>, St0) of
Meta when Meta =/= nil ->
{Ret,St1} = functioncall(Meta, [O1,O2], St0),
- {[is_true(Ret)],St1};
+ {[is_true_value(Ret)],St1};
nil ->
%% Try for not (Op2 < Op1) instead.
Meta = getmetamethod(O1, O2, <<"__lt">>, St0),
{Ret,St1} = functioncall(Meta, [O2,O1], St0),
- {[not is_true(Ret)],St1}
+ {[not is_true_value(Ret)],St1}
end.
%% getmetamethod(Object1, Object2, Event, State) -> Metod | nil.
@@ -1025,19 +1043,18 @@ getmetamethod_tab(#tref{i=M}, E, Ts) ->
end;
getmetamethod_tab(_, _, _) -> nil. %Other types have no metatables
-%% is_true(Rets) -> boolean().
+%% is_true_value(Rets) -> boolean().
-is_true([nil|_]) -> false;
-is_true([false|_]) -> false;
-is_true([_|_]) -> true;
-is_true([]) -> false.
+is_true_value([nil|_]) -> false;
+is_true_value([false|_]) -> false;
+is_true_value([_|_]) -> true;
+is_true_value([]) -> false.
+
+%% first_value(Rets) -> Value.
first_value([V|_]) -> V;
first_value([]) -> nil.
-badarg_error(What, Args) ->
- lua_error({badarg,What,Args}).
-
illegal_val_error(Val) ->
lua_error({illegal_val,Val}).
View
31 src/luerl_lib.erl
@@ -35,13 +35,18 @@
-include("luerl.hrl").
--export([lua_error/1,format_error/1,is_true/1,first_value/1,number_to_list/1,
+-export([lua_error/1,badarg_error/2,format_error/1,
+ is_true_value/1,first_value/1,number_to_list/1,
to_list/1,to_lists/1,to_lists/2,to_int/1,to_ints/1,to_ints/2,
tonumber/1,tonumber/2,tonumbers/1,tonumbers/2,tointeger/1,
tointegers/1,tointegers/2,tostring/1,tostrings/1,tostrings/2,
conv_list/2,conv_list/3]).
--export([anew/1,aget/2,aset/3,aclr/2,asl/3,asr/3]).
+-export([anew/1,asiz/1,aget/2,aset/3,aclr/2,asl/3,asr/3]).
+
+-spec lua_error(_) -> no_return().
+
+badarg_error(What, Args) -> lua_error({badarg,What,Args}).
lua_error(E) -> error({lua_error,E}).
@@ -51,6 +56,11 @@ lua_error(E) -> error({lua_error,E}).
anew(_) -> [].
+asiz(A) -> asiz(A, 0).
+
+asiz([{_,L,_}|A], _) -> asiz(A, L);
+asiz([], S) -> S.
+
aget(I, [{I,_,Es}|_]) -> hd(Es); %First element
aget(I, [{F,L,Es}|_]) when I >= F, I =< L -> %It's in here
lists:nth(I-F+1, Es);
@@ -58,6 +68,7 @@ aget(I, [{_,L,_}|A]) when I > L -> %Not yet
aget(I, A);
aget(_, _) -> nil. %Not at all
+aset(I, nil, A) -> aclr(I, A); %Setting to nil is clearing
aset(I, V, [{F,L,Es}|A]) when I >= F, I =< L -> %Set it here
[{F,L,setnth(I-F+1, V, Es)}|A];
aset(I, V, [{F,L,Es}]) when I =:= L+1, F-L < 10 ->
@@ -91,8 +102,12 @@ setnth(N, V, [E|Es]) -> [E|setnth(N-1, V, Es)].
%% Some of these use same text as Lua error string, so be careful if
%% modifying them.
+format_error({undefined_method, Name, Args0, Line}) ->
+ io_lib:format("undefined_method ~w with args: ~p on line ~p", [Name, Args0, Line]);
format_error({badarg,Where,As}) ->
io_lib:format("badarg in ~w: ~w", [Where,As]);
+format_error({method_on_nil, Key}) ->
+ io_lib:format("undefined method ~w on nil", [Key]);
format_error({illegal_key,Tab,Key}) ->
io_lib:format("invalid key in ~w: ~w", [Tab,Key]);
format_error({illegal_index,Where,I}) ->
@@ -105,6 +120,8 @@ format_error({illegal_comp,Where}) ->
io_lib:format("illegal comparison in ~w", [Where]);
format_error({invalid_order,Where}) -> %Keep text!
io_lib:format("invalid order function in ~w", [Where]);
+format_error({undef_function,Name}) ->
+ io_lib:format("undefined function ~w", [Name]);
%% Pattern errors.
format_error(invalid_pattern) -> %Keep text!
io_lib:format("malformed pattern", []);
@@ -120,13 +137,13 @@ format_error({illegal_op,Op}) ->
format_error({undefined_op,Op}) ->
io_lib:format("undefined op: ~w", [Op]).
-%% is_true(Rets) -> boolean()>
+%% is_true_value(Rets) -> boolean()>
%% first_value(Rets) -> Value | nil.
-is_true([nil|_]) -> false;
-is_true([false|_]) -> false;
-is_true([_|_]) -> true;
-is_true([]) -> false.
+is_true_value([nil|_]) -> false;
+is_true_value([false|_]) -> false;
+is_true_value([_|_]) -> true;
+is_true_value([]) -> false.
first_value([V|_]) -> V;
first_value([]) -> nil.
View
196 src/luerl_table.erl
@@ -29,13 +29,14 @@
%% 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 an orddict!
+%% that a table is an orddict! We know that the erlang array has
+%% default value 'nil'.
-module(luerl_table).
-include("luerl.hrl").
--export([install/1,length/2,test_insert/2,test_insert/3]).
+-export([install/1,length/2,test_insert/2,test_insert/3,test_concat/1]).
-import(luerl_lib, [lua_error/1]). %Shorten this
@@ -55,37 +56,59 @@ table() ->
%% concat - concat the elements of a list into a string.
-concat([#tref{}=T], St) -> concat(T, <<>>, [1.0], St);
-concat([#tref{}=T,A2], St) -> concat(T, A2, [1.0], St);
-concat([#tref{}=T,A2|As], St) -> concat(T, A2, As, St);
+concat([#tref{i=N}|As], St) ->
+ #table{a=Arr,t=Tab} = ?GET_TABLE(N, St#luerl.tabs),
+ case luerl_lib:conv_list(concat_args(As), [lstring,linteger,linteger]) of
+ [Sep,I] ->
+ {[concat(Arr, Tab, Sep, I, length_loop(Arr))],St};
+ [Sep,I,J] ->
+ {[concat(Arr, Tab, Sep, I, J)],St}
+ end;
concat(As, _) -> lua_error({badarg,concat,As}).
-concat(#tref{i=N}=T, A2, As, St) ->
- #table{a=Arr} = ?GET_TABLE(N, St#luerl.tabs),
- case luerl_lib:tostrings([A2], luerl_lib:to_ints(As)) of
- [Sep|Is] -> {[concat(Arr, Sep, Is)],St};
- _ -> lua_error({badarg,concat,[T,A2|As]})
- end.
+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].
-concat(Arr, Sep, [I]) ->
- Conc = concat_loop(Arr, I, length_loop(Arr, 1)),
- concat_join(Conc, Sep);
-concat(Arr, Sep, [I,J|_]) ->
- Conc = concat_loop(Arr, I, J),
+concat(Arr, Tab, Sep, I, J) ->
+ Conc = concat_table(Arr, Tab, I, J),
concat_join(Conc, Sep).
+concat_table(Arr, Tab, I, J) ->
+ concat_tab(Arr, Tab, I, J).
+
%% This and unpack_loop are very similar.
-concat_loop(_, N, J) when N > J -> []; %Done
-concat_loop([{N,V}|Tab], N, J) ->
+%% 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, round(N), J); %Need integers keys now
+concat_tab(Arr, [{K,V}|Tab], N, J) when K == N ->
case luerl_lib:to_list(V) of
nil -> lua_error({illegal_val,concat,V});
- S -> [S|concat_loop(Tab, N+1, J)]
+ S -> [S|concat_tab(Arr, Tab, N+1, J)]
end;
-concat_loop([{K,_}|_], N, _) when K > N -> %Gap
+concat_tab(Arr, [{K,_}|_], N, _) when K > N -> %Gap
lua_error({illegal_val,concat,nil});
-concat_loop([{K,_}|Tab], N, J) when K < N ->
- concat_loop(Tab, N, J);
-concat_loop([], _, _) -> lua_error({illegal_val,concat,nil}).
+concat_tab(Arr, [{K,_}|Tab], N, J) when K < N -> %Step over too small keys
+ concat_tab(Arr, Tab, N, J);
+concat_tab(_, [], _, _) -> lua_error({illegal_val,concat,nil}).
+
+concat_arr(_, N, J) when N > J -> [];
+concat_arr(Arr, N, J) ->
+ V = array:get(N, Arr),
+ case luerl_lib:to_list(V) of
+ nil -> lua_error({illegal_val,concat,V});
+ S -> [S|concat_arr(Arr, N+1, J)]
+ end.
concat_join([E], _) -> list_to_binary(E);
concat_join([E1|Es], Sep) ->
@@ -102,12 +125,12 @@ insert([#tref{i=N},V], St0) ->
{[],St0#luerl{tabs=Ts1}};
insert([#tref{i=N},P0,V]=As, St0) ->
Ts0 = St0#luerl.tabs,
- #table{a=Arr0}=T = ?GET_TABLE(N, Ts0),
+ #table{a=Arr0,t=Tab0}=T = ?GET_TABLE(N, Ts0),
case luerl_lib:to_int(P0) of
nil -> lua_error({badarg,insert,As});
P1 ->
- Arr1 = do_insert(Arr0, P1, V),
- Ts1 = ?SET_TABLE(N, T#table{a=Arr1}, Ts0),
+ {Arr1,Tab1} = do_insert(Arr0, Tab0, P1, V),
+ Ts1 = ?SET_TABLE(N, T#table{a=Arr1,t=Tab1}, Ts0),
{[],St0#luerl{tabs=Ts1}}
end;
insert(As, _) -> lua_error({badarg,insert,As}).
@@ -130,9 +153,14 @@ insert(As, _) -> lua_error({badarg,insert,As}).
test_insert(T, V) -> do_insert_last(T, V).
test_insert(T, N, V) -> do_insert(T, N, V).
-%% do_insert(Arr, N, V) -> Arr.
+%% do_insert(Arr, Tab, N, V) -> Arr.
%% Don't ask, it tries to emulate the "real" Lua.
+do_insert(Arr, Tab, N, V) when N >= 1 ->
+ {insert_shift_arr(Arr, N, V),Tab};
+do_insert(Arr, Tab, N, V) ->
+ {Arr,Tab}.
+
do_insert([{K,nil}|_], _, _) -> %Shouldn't be a nil
error({boom,K,nil});
do_insert([{N,_}|_]=Arr, N, V) -> %Push it in here
@@ -147,13 +175,21 @@ insert_renum([{K,nil}|_], _) -> %Shouldn't be a nil
insert_renum([{N,V}|Arr], N) -> [{N+1,V}|insert_renum(Arr, N+1)];
insert_renum(Arr, _) -> Arr. %Gap or end of list
+insert_shift_arr(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_shift_arr(Arr1, N+1, Next)
+ end.
+
do_insert_last(Arr, V) -> do_insert_last(Arr, 1, V).
-do_insert_last([{K,nil}|_], _, _) -> %Shouldn't be a nil
- error({boom,K,nil});
-do_insert_last([{N,_}=P|Arr], N, V) ->
- [P|do_insert_last(Arr, N+1, V)];
-do_insert_last(Arr, N, V) -> [{N,V}|Arr]. %Gap or end of list
+do_insert_last(Arr, N, V) -> %Find first nil
+ case array:get(N, Arr) of
+ nil -> array:set(N, V, Arr);
+ _ -> do_insert_last(Arr, N+1, V)
+ end.
%% pack - pack arguments in to a table.
@@ -213,14 +249,14 @@ remove_renum(Arr, _) -> Arr. %Gap or end of list
%% unpack - unpack table into return values.
unpack([#tref{i=N}=T|As], St) ->
- #table{a=Arr} = ?GET_TABLE(N, St#luerl.tabs),
+ #table{a=Arr,t=Tab} = ?GET_TABLE(N, St#luerl.tabs),
case luerl_lib:to_ints(unpack_args(As)) of
[I] ->
- Unp = unpack_loop(Arr, I, length_loop(Arr, 1)),
+ Unp = unpack_table(Arr, Tab, I, length_loop(Arr)),
%% io:fwrite("unp: ~p\n", [{Arr,I,Start,Unp}]),
{Unp,St};
[I,J] ->
- Unp = unpack_loop(Arr, I, J),
+ Unp = unpack_table(Arr, Tab, I, J),
%% io:fwrite("unp: ~p\n", [{Arr,I,J,Start,Unp}]),
{Unp,St};
_ -> lua_error({badarg,unpack,[T|As]})
@@ -233,15 +269,27 @@ unpack_args([I]) -> [I]; %Only one argument
unpack_args([I,nil|_]) -> [I]; %Goto the default end
unpack_args([I,J|_]) -> [I,J]. %Two arguments
-%% This and concat_loop are very similar.
-unpack_loop(_, N, J) when N > J -> []; %Done
-unpack_loop([{N,V}|Tab], N, J) ->
- [V|unpack_loop(Tab, N+1, J)];
-unpack_loop([{K,_}|_]=Tab, N, J) when K > N -> %Gap
- [nil|unpack_loop(Tab, N+1, J)];
-unpack_loop([{K,_}|Tab], N, J) when K < N ->
- unpack_loop(Tab, N, J);
-unpack_loop([], N, J) -> [nil|unpack_loop([], N+1, J)].
+%% 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.
+
+unpack_table(Arr, Tab, I, J) -> unpack_tab(Arr, Tab, I, J).
+
+unpack_tab(_, _, N, J) when N > J -> []; %Done
+unpack_tab(Arr, _, N, J) when N > 0 -> %Done with table
+ unpack_arr(Arr, round(N), J); %Need integer keys now
+unpack_tab(Arr, [{K,V}|Tab], N, J) when K == N ->
+ [V|unpack_tab(Arr, Tab, N+1, J)];
+unpack_tab(Arr, [{K,_}|_]=Tab, N, J) when K > N -> %Gap
+ [nil|unpack_tab(Arr, Tab, N+1, J)];
+unpack_tab(Arr, [{K,_}|Tab], N, J) when K < N -> %Step over too small keys
+ unpack_tab(Arr, Tab, N, J);
+unpack_tab(Arr, [], N, J) -> [nil|unpack_tab(Arr, [], 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)].
%% sort - sort the elements of the list after their values.
@@ -276,7 +324,7 @@ lt_comp(O1, O2, St0) ->
nil -> lua_error({illegal_comp,sort});
Meta ->
{Ret,St1} = luerl_eval:functioncall(Meta, [O1,O2], St0),
- {[is_true(Ret)],St1}
+ {[luerl_lib:is_true_value(Ret)],St1}
end.
renumber(Tab0) ->
@@ -296,23 +344,19 @@ length(#tref{i=N}=T, St) ->
{[float(length_loop(Arr))],St}
end.
-length_loop([{2,_}|Arr]) -> length_loop(Arr, 2);
-length_loop(Arr) -> length_loop(Arr, 1).
-
-length_loop([{K,_}|Arr], N) when K < N -> length_loop(Arr, N);
-length_loop([{N,V}|Arr], N) when V =/= nil -> length_loop(Arr, N+1);
-length_loop(_, N) -> N-1. %Hit a nil or gap
-
-%% drop_until([{K,_}|Tab], N) when K < N ->
-%% drop_until(Tab, N);
-%% drop_until(Tab, _) -> Tab.
-
-%% is_true(Rets) -> boolean().
+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.
-is_true([nil|_]) -> false;
-is_true([false|_]) -> false;
-is_true([_|_]) -> true;
-is_true([]) -> false.
+length_loop(I, Arr) ->
+ case array:get(I, Arr) of
+ nil -> I-1;
+ _ -> length_loop(I+1, Arr)
+ end.
%% sort(A,B,C) -> sort_up(A,B,C).
@@ -368,7 +412,7 @@ merge_sort(_, St, []) -> {[],St};
merge_sort(_, St, [_] = L) -> {L,St};
merge_sort(Fun, St0, [X, Y|T]) ->
{Ret,St1} = Fun([X,Y], St0),
- case is_true(Ret) of
+ case luerl_lib:is_true_value(Ret) of
true ->
fsplit_1(Y, X, Fun, St1, T, [], []);
false ->
@@ -378,12 +422,12 @@ merge_sort(Fun, St0, [X, Y|T]) ->
%% Ascending.
fsplit_1(Y, X, Fun, St0, [Z|L], R, Rs) ->
{Ret1,St1} = Fun([Y,Z], St0),
- case is_true(Ret1) of
+ case luerl_lib:is_true_value(Ret1) of
true ->
fsplit_1(Z, Y, Fun, St1, L, [X|R], Rs);
false ->
{Ret2,St2} = Fun([X,Z], St1),
- case is_true(Ret2) of
+ case luerl_lib:is_true_value(Ret2) of
true ->
fsplit_1(Y, Z, Fun, St2, L, [X|R], Rs);
false when R == [] ->
@@ -397,17 +441,17 @@ fsplit_1(Y, X, Fun, St, [], R, Rs) ->
fsplit_1_1(Y, X, Fun, St0, [Z|L], R, Rs, S) ->
{Ret1,St1} = Fun([Y,Z], St0),
- case is_true(Ret1) of
+ case luerl_lib:is_true_value(Ret1) of
true ->
fsplit_1_1(Z, Y, Fun, St1, L, [X|R], Rs, S);
false ->
{Ret2,St2} = Fun([X,Z], St1),
- case is_true(Ret2) of
+ case luerl_lib:is_true_value(Ret2) of
true ->
fsplit_1_1(Y, Z, Fun, St2, L, [X|R], Rs, S);
false ->
{Ret3,St3} = Fun([S,Z], St2),
- case is_true(Ret3) of
+ case luerl_lib:is_true_value(Ret3) of
true ->
fsplit_1(Z, S, Fun, St3, L, [], [[Y, X|R]|Rs]);
false ->
@@ -421,12 +465,12 @@ fsplit_1_1(Y, X, Fun, St, [], R, Rs, S) ->
%% Descending.
fsplit_2(Y, X, Fun, St0, [Z|L], R, Rs) ->
{Ret1,St1} = Fun([Y,Z], St0),
- case is_true(Ret1) of
+ case luerl_lib:is_true_value(Ret1) of
false ->
fsplit_2(Z, Y, Fun, St1, L, [X|R], Rs);
true ->
{Ret2,St2} = Fun([X,Z], St1),
- case is_true(Ret2) of
+ case luerl_lib:is_true_value(Ret2) of
false ->
fsplit_2(Y, Z, Fun, St2, L, [X|R], Rs);
true when R == [] ->
@@ -440,17 +484,17 @@ fsplit_2(Y, X, Fun, St, [], R, Rs) ->
fsplit_2_1(Y, X, Fun, St0, [Z|L], R, Rs, S) ->
{Ret1,St1} = Fun([Y,Z], St0),
- case is_true(Ret1) of
+ case luerl_lib:is_true_value(Ret1) of
false ->
fsplit_2_1(Z, Y, Fun, St1, L, [X|R], Rs, S);
true ->
{Ret2,St2} = Fun([X,Z], St1),
- case is_true(Ret2) of
+ case luerl_lib:is_true_value(Ret2) of
false ->
fsplit_2_1(Y, Z, Fun, St2, L, [X|R], Rs, S);
true ->
{Ret3,St3} = Fun([S,Z], St2),
- case is_true(Ret3) of
+ case luerl_lib:is_true_value(Ret3) of
false ->
fsplit_2(Z, S, Fun, St3, L, [], [[Y, X|R]|Rs]);
true ->
@@ -492,7 +536,7 @@ rfmergel([], Acc, Fun, St, O) ->
%% Elements from the first list are prioritized.
fmerge2_1([H1|T1], H2, Fun, St0, T2, M) ->
{Ret,St1} = Fun([H1,H2], St0),
- case is_true(Ret) of
+ case luerl_lib:is_true_value(Ret) of
true ->
fmerge2_1(T1, H2, Fun, St1, T2, [H1|M]);
false ->
@@ -503,7 +547,7 @@ fmerge2_1([], H2, _Fun, St, T2, M) ->
fmerge2_2(H1, T1, Fun, St0, [H2|T2], M) ->
{Ret,St1} = Fun([H1,H2], St0),
- case is_true(Ret) of
+ case luerl_lib:is_true_value(Ret) of
true ->
fmerge2_1(T1, H2, Fun, St1, T2, [H1|M]);
false ->
@@ -519,7 +563,7 @@ fmerge2_2(H1, T1, _Fun, St, [], M) ->
rfmerge2_1([H1|T1], H2, Fun, St0, T2, M) ->
{Ret,St1} = Fun([H1,H2], St0),
- case is_true(Ret) of
+ case luerl_lib:is_true_value(Ret) of
true ->
rfmerge2_2(H1, T1, Fun, St1, T2, [H2|M]);
false ->
@@ -530,7 +574,7 @@ rfmerge2_1([], H2, _Fun, St, T2, M) ->
rfmerge2_2(H1, T1, Fun, St0, [H2|T2], M) ->
{Ret,St1} = Fun([H1,H2], St0),
- case is_true(Ret) of
+ case luerl_lib:is_true_value(Ret) of
true ->
rfmerge2_2(H1, T1, Fun, St1, T2, [H2|M]);
false ->

0 comments on commit f782f3d

Please sign in to comment.
Something went wrong with that request. Please try again.