diff --git a/README b/README index 0ecf0143..a5c2a643 100644 --- a/README +++ b/README @@ -5,3 +5,18 @@ code. An LFE evaluator and shell is also included. This is the first version with the modified internal core forms and macro intefaces for the new CL inspired style and the older Scheme inspired style. + +Two new modules have been added: + +lfe_boot allows you start Erlang with the LFE shell running and still +have ^G enabled and user_drv running. Use it as follows: + +erl -noshell -noinput -s lfe_boot start + +NOTE order of commands important, must be -noshell -noinput! Add -pa +to find modules if necessary. + +lfe_gen is a trial interface for using LFE for dynamic code +generation. LFE is much easier to generate as an Erkang list than +Erlang forms. This module helps defining and compiling a module. Note, +that while it works, this module is very experimental and may change. diff --git a/ebin/lfe_boot.beam b/ebin/lfe_boot.beam new file mode 100644 index 00000000..332fd0bb Binary files /dev/null and b/ebin/lfe_boot.beam differ diff --git a/ebin/lfe_comp.beam b/ebin/lfe_comp.beam index 1c86113a..0d5d3496 100644 Binary files a/ebin/lfe_comp.beam and b/ebin/lfe_comp.beam differ diff --git a/ebin/lfe_gen.beam b/ebin/lfe_gen.beam new file mode 100644 index 00000000..ee41fb5e Binary files /dev/null and b/ebin/lfe_gen.beam differ diff --git a/ebin/lfe_io.beam b/ebin/lfe_io.beam index 5616d81f..d06088d9 100644 Binary files a/ebin/lfe_io.beam and b/ebin/lfe_io.beam differ diff --git a/src/ChangeLog b/src/ChangeLog index afe06b3c..d72ccf58 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2008-09-28 + + * lfe_gen.erl: Support for dynamic code generation. + + * lfe_boot.erl: Start Erlang with the LFE shell running. + + * lfe_io.erl (print1_bits): Allow printing of some of the bytes in + a bitstring. + 2008-09-27 * lfe_macro.erl (macro): Apply function of course. diff --git a/src/lfe_boot.erl b/src/lfe_boot.erl new file mode 100644 index 00000000..1ab7eea2 --- /dev/null +++ b/src/lfe_boot.erl @@ -0,0 +1,44 @@ +%% Copyright (c) 2008 Robert Virding. All rights reserved. +%% +%% Redistribution and use in source and binary forms, with or without +%% modification, are permitted provided that the following conditions +%% are met: +%% +%% 1. Redistributions of source code must retain the above copyright +%% notice, this list of conditions and the following disclaimer. +%% 2. Redistributions in binary form must reproduce the above copyright +%% notice, this list of conditions and the following disclaimer in the +%% documentation and/or other materials provided with the distribution. +%% +%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +%% COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +%% POSSIBILITY OF SUCH DAMAGE. + +%%% File : lfe_boot.erl +%%% Author : Robert Virding +%%% Purpose : Lisp Flavoured Erlang boot module. + +%%% This little beauty allows you to start Erlang with the LFE shell +%%% running and still has ^G and user_drv enabled. Use it as follows: +%%% +%%% erl -noshell -noinput -s lfe_boot start +%%% +%%% NOTE order of commands important, must be -noshell -noinput! Add +%%% -pa to find modules if necessary. +%%% +%%% Thanks to Attila Babo for showing me how to do this. + +-module(lfe_boot). + +-export([start/0]). + +start() -> user_drv:start(['tty_sl -c -e',{lfe_shell,start,[]}]). diff --git a/src/lfe_comp.erl b/src/lfe_comp.erl index 43708b9d..e99de7e7 100644 --- a/src/lfe_comp.erl +++ b/src/lfe_comp.erl @@ -125,10 +125,10 @@ do_return(Core, Warns, St) -> forms(Forms) -> forms(Forms, []). %Default options. forms(Forms, Opts) -> case forms(Forms, #comp{}, Opts) of - {ok,Core,_,St} -> - {ok,Bin} = compile:forms(Core, - [from_core,return_errors|St#comp.opts]), - {ok,St#comp.mod,Bin}; + {ok,Core,Ws,St} -> + {ok,_,Bin} = + compile:forms(Core, [from_core,return_errors|St#comp.opts]), + {ok,St#comp.mod,Bin,Ws}; {error,Es,Ws,_} -> {error,Es,Ws} end. diff --git a/src/lfe_gen.erl b/src/lfe_gen.erl new file mode 100644 index 00000000..8abbd7c4 --- /dev/null +++ b/src/lfe_gen.erl @@ -0,0 +1,114 @@ +%% Copyright (c) 2008 Robert Virding. All rights reserved. +%% +%% Redistribution and use in source and binary forms, with or without +%% modification, are permitted provided that the following conditions +%% are met: +%% +%% 1. Redistributions of source code must retain the above copyright +%% notice, this list of conditions and the following disclaimer. +%% 2. Redistributions in binary form must reproduce the above copyright +%% notice, this list of conditions and the following disclaimer in the +%% documentation and/or other materials provided with the distribution. +%% +%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +%% COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +%% POSSIBILITY OF SUCH DAMAGE. + +%%% File : lfe_gen.erl +%%% Author : Robert Virding +%%% Purpose : Lisp Flavoured Erlang dynamic code generator. + +-module(lfe_gen). + +-export([compile_forms/1]). +-export([new_module/1,add_exports/2,add_imports/2,add_form/2, + print_mod/1,compile_mod/1]). + +-import(lists, [map/2,foldl/3,mapfoldl/3]). +-import(ordsets, [add_element/2]). +-import(orddict, [store/3,find/2]). + +-record(gen, {name,exps=[],imps=[],atts=[],forms=[]}). + +%% compile_forms(Forms) -> {ok,Name,Bin,Warns} | {error,Errors,Warns}. +%% Compile all LFE module forms in one go. + +compile_forms(Fs0) -> + %% Tag forms with a "line number", just use their index. + {Fs1,_} = mapfoldl(fun (F, N) -> {{F,N},N+1} end, 1, Fs0), + case lfe_comp:forms(Fs1, []) of + {ok,Mod,Bin,Ws} -> {ok,Mod,Bin,Ws}; + {error,Es,Ws} -> {error,Es,Ws} + end. + +%% new_module(Name) -> Module. +%% add_exports([{Name,Arity}], Module) -> Module. +%% add_imports({from,Mod,[{Name,Arity}]}, Module) -> Module. +%% add_form(Form, Module) -> Module. +%% print_mod(Module) -> iolist(). +%% compile_mod(Mod) -> {ok,Name,Bin,Warns} | {error,Errors,Warns}. +%% The incremental interface to compiling a module. + +new_module(Name) -> + #gen{name=Name,forms=[]}. + +add_exports(Exps, Mod) -> + Es0 = Mod#gen.exps, + Es1 = foldl(fun ({N,Ar}, Es) when is_atom(N), is_integer(Ar) -> + add_element({N,Ar}, Es) + end, Es0, Exps), + Mod#gen{exps=Es1}. + +add_imports({from,M,Is}, Mod) -> + Imps0 = Mod#gen.imps, + Imps1 = collect_imp(fun ({F,A}, Imps) -> store({F,A}, F, Imps) end, + M, Imps0, Is), + Mod#gen{imps=Imps1}; +add_imports({rename,M,Is}, Mod) -> + Imps0 = Mod#gen.imps, + Imps1 = collect_imp(fun ({{F,A},R}, Imps) -> store({F,A}, R, Imps) end, + M, Imps0, Is), + Mod#gen{imps=Imps1}. + +add_form(Form, Mod) -> + Mod#gen{forms=Mod#gen.forms ++ [Form]}. + +compile_mod(Mod) -> + Fs = [build_def(Mod)|Mod#gen.forms], + compile_forms(Fs). + +print_mod(Mod) -> %Needs fixing + map(fun (F) -> [lfe_io:prettyprint1(F, 0),io_lib:nl()] end, + [build_def(Mod)|Mod#gen.forms]). + +collect_imp(Fun, Mod, Imps, Is) -> + Mimps0 = safe_fetch(Mod, Imps, []), + Mimps1 = foldl(Fun, Mimps0, Is), + store(Mod, Mimps1, Imps). + +build_def(Mod) -> + Exps = map(fun ({N,I}) -> [N,I] end, Mod#gen.exps), + Imps = map(fun ({M,Is}) -> + [rename,M|map(fun ({{L,Ar},R}) -> [[L,Ar],R] end, + Is)] + end, Mod#gen.imps), + [defmodule,Mod#gen.name, + [export|Exps], + [import|Imps]]. + +%% safe_fetch(Key, Dict, Default) -> Value. + +safe_fetch(Key, D, Def) -> + case find(Key, D) of + {ok,Val} -> Val; + error -> Def + end. diff --git a/src/lfe_io.erl b/src/lfe_io.erl index 98733043..23032838 100644 --- a/src/lfe_io.erl +++ b/src/lfe_io.erl @@ -146,11 +146,14 @@ print1_symb(Symb) -> %% Print the bytes in a bitstring. Print bytes except for last which %% we print as bitstring segement if not 8 bits big. -print1_bits(<>) -> integer_to_list(B); %Catch last binary byte -print1_bits(<>) -> - [integer_to_list(B),$\s|print1_bits(Bits)]; -print1_bits(<<>>) -> []; -print1_bits(Bits) -> %0 < Size < 8 +print1_bits(Bits) -> print1_bits(Bits, -1). %Print them all + +print1_bits(_, 0) -> "..."; +print1_bits(<>, _) -> integer_to_list(B); %Catch last binary byte +print1_bits(<>, N) -> + [integer_to_list(B),$\s|print1_bits(Bits, N-1)]; +print1_bits(<<>>, _) -> []; +print1_bits(Bits, _) -> %0 < Size < 8 N = bit_size(Bits), <> = Bits, io_lib:format("(~w bitstring (size ~w))", [B,N]). @@ -288,7 +291,7 @@ prettyprint1(Tup, I) when is_tuple(Tup) -> ["#(",prettyprint1(hd(List), I+2),pp_tail(tl(List), I+2),")"] end; prettyprint1(Bit, _) when is_bitstring(Bit) -> - ["#B(",print1_bits(Bit),$)]. + ["#B(",print1_bits(Bit, 30),$)]. %First 30 bytes %% split(N, List) -> {List1,List2}. %% Split a list into two lists, the first containing the first N @@ -323,8 +326,7 @@ indent_type('let-syntax') -> 1; indent_type('syntax-rules') -> 0; indent_type('macro') -> 0; %% New style functions. -indent_type('define-function') -> 1; -indent_type('define-macro') -> 1; +indent_type('defmodule') -> 1; indent_type('defun') -> 1; indent_type('defmacro') -> 1; indent_type('defsyntax') -> 1; @@ -343,6 +345,8 @@ indent_type('cond') -> 999; %All following forms indent_type('catch') -> 0; indent_type('try') -> 1; indent_type('call') -> 2; +indent_type('define-function') -> 1; +indent_type('define-macro') -> 1; %% Core macros. indent_type('let*') -> 1; indent_type('flet') -> 1;