Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
andyfinnell committed Jan 17, 2016
0 parents commit 63d315b
Show file tree
Hide file tree
Showing 17 changed files with 1,121 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .gitignore
@@ -0,0 +1,13 @@
.eunit
deps
*.o
*.beam
*.plt
erl_crash.dump
ebin
bin
rel/example_project
.concrete/DEV_MODE
.rebar
.deps_plt
logs
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2016 Andrew J. Finnell

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.

39 changes: 39 additions & 0 deletions Makefile
@@ -0,0 +1,39 @@
REBAR:=rebar

DEPS_PLT=$(CURDIR)/.deps_plt
DEPS_DIR=$(CURDIR)/deps
BIN_DIR=$(CURDIR)/bin
LIB_DIR=$(BIN_DIR)/lib

DEPS=erts kernel stdlib

.PHONY: all erl test clean doc dialyzer dep_compile

all: erl

dep_compile:
$(REBAR) get-deps compile

erl: dep_compile
$(REBAR) escriptize

test: all
@mkdir -p .eunit
$(REBAR) skip_deps=true ct

clean:
$(REBAR) clean
-rm -rvf $(DEPS_DIR) ebin bin doc .eunit logs
-rm -rvf test/*.beam

doc:
$(REBAR) doc

$(DEPS_PLT):
@echo Building local plt at $(DEPS_PLT)
@echo
dialyzer --output_plt $(DEPS_PLT) --build_plt \
--apps $(DEPS) -r $(DEPS_DIR)

dialyzer: $(DEPS_PLT)
dialyzer --fullpath --plt $(DEPS_PLT) -Wrace_conditions -r ./ebin
44 changes: 44 additions & 0 deletions README.md
@@ -0,0 +1,44 @@
# egret

##Description

Egret is an Ocaml-like language targeting Erlang's BEAM. It is a learning tool, not production software.

##Dependencies

* rebar
* make
* erlando (automatically downloaded and built by rebar)

##Build

To build the egret compiler just run make.

$ make

This will place the `egret` executable in the `bin` subdirectory.

##Use

The `egret` compiler can only take one parameter, the file to compile. For example:

$ ./bin/egret test/add.egret

This will generate a BEAM file in the same directory as the source file. In this case it will be `test/add.beam`. From here, the BEAM file can be used just like any other; all functions are exported by default.

$ cd test
$ erl
Eshell V7.2 (abort with ^G)
$ 1> l(add).
{module,add}

The above moves to the `test` directory, starts the Erlang shell, and loads the `add` BEAM file just built. To call a function:

$ 2> add:add(3.0).
5.0

Also, all egret modules export a `main/0` function that executes all the top level expressions.

$ 3> add:main().
5.0

5 changes: 5 additions & 0 deletions rebar.config
@@ -0,0 +1,5 @@
{deps, [
{erlando, ".*", {git, "https://github.com/travelping/erlando.git", ""}}
]}.
{escript_name, "bin/egret"}.
{escript_incl_apps, [erlando]}.
15 changes: 15 additions & 0 deletions src/egret.app.src
@@ -0,0 +1,15 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

{application, egret, [
{description, "Egret compiler targeting BEAM."},
{vsn, git },
{modules, []},
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, { egret, []}}
]}.

50 changes: 50 additions & 0 deletions src/egret.erl
@@ -0,0 +1,50 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

-module(egret).

-export([main/1]).

%% Public API

-spec main(list()) -> ok.
main(Args) ->
execute(Args).

%% Implementation

-spec execute(list()) -> ok.
execute([Filename]) ->
process_parse(egret_compiler:file(Filename), Filename);
execute(_Args) ->
usage().

process_parse({ok, ModuleName, Binary, Warnings}, Filename) ->
ok = write_beam(filename:dirname(Filename), ModuleName, Binary),
print_errors(Warnings, "Warning", Filename),
io:format("Success: ~p.~n", [ModuleName]);
process_parse({error, {Errors, Warnings}}, Filename) ->
print_errors(Errors, "Error", Filename),
print_errors(Warnings, "Warning", Filename),
io:format("Failure.~n").

print_errors(Errors, Type, Filename) ->
PrintError = fun(Info) -> print_error(Info, Type, Filename) end,
lists:foreach(PrintError, Errors).

print_error({_Filename, ErrorInfos}, Type, Filename) ->
PrintError = fun(Info) -> print_error_info(Info, Type, Filename) end,
lists:foreach(PrintError, ErrorInfos).

print_error_info({Line, Module, Descriptor}, Type, Filename) ->
ErrorString = Module:format_error(Descriptor),
io:format("~s:~p: ~s: ~s~n", [Filename, Line, Type, ErrorString]).

write_beam(Dirname, ModuleName, Binary) ->
Filename = filename:join(Dirname, string:concat(atom_to_list(ModuleName), ".beam")),
file:write_file(Filename, Binary).

-spec usage() -> ok.
usage() ->
io:format("Usage: egret <path-to-compile>...~n"),
ok.
32 changes: 32 additions & 0 deletions src/egret_ast.erl
@@ -0,0 +1,32 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

-module(egret_ast).

-export([prototype_name/1, prototype_args/1]).

-type lineno() :: integer().
-type variable() :: {variable, lineno(), string()}.


%% Base type for all expression nodes.
-type expr() ::
%% variant for numeric literals like "1.0".
{real, lineno(), float()}.

%% Function prototype. Name and arguments
-type proto() :: {prototype, lineno(), string(), [variable()]}.

%% Function definition
-type func() :: {function, lineno(), proto(), [expr()], atom() | none}.

-type egret_module() :: {module, lineno(), string(), [func()]}.

-export_type([expr/0, proto/0, func/0, egret_module/0, lineno/0]).

%% Helper functions

prototype_name({prototype, _Line, Name, _FormalArgs}) -> Name.

prototype_args({prototype, _Line, _Name, FormalArgs}) -> FormalArgs.

25 changes: 25 additions & 0 deletions src/egret_binarygen.erl
@@ -0,0 +1,25 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

-module(egret_binarygen).

-export([forms/1, format_error/1]).

%% Public API

forms(AbsForms) ->
process_binary(compile:forms(AbsForms, [verbose, return, export_all])).

format_error(unknown) ->
"Unknown compile error".

%% Implementation

process_binary({ok,ModuleName,BinaryOrCode}) ->
{ok, ModuleName, BinaryOrCode, []};
process_binary(Ret = {ok, _ModuleName, _BinaryOrCode, _Warnings}) ->
Ret;
process_binary(error) ->
{error, {[{"<<internal>>", [{none, ?MODULE, unknown}]}], []}};
process_binary({error, Errors, Warnings}) ->
{error, {Errors, Warnings}}.
42 changes: 42 additions & 0 deletions src/egret_codegen.erl
@@ -0,0 +1,42 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

-module(egret_codegen).

-export([module/1]).

%% Public API

module({module, Line, ModuleName, Functions}) ->
ModuleForm = {attribute, Line, module, list_to_atom(ModuleName)},
{ok, [ModuleForm | functions(Functions, [])]}.

%% Implementation

functions([], Accumulator) ->
Accumulator;
functions([Function | Rest], Accumulator) ->
functions(Rest, [function(Function) | Accumulator]).

function(F = {function, Line, Prototype, _Exprs, _Module}) ->
Args = egret_ast:prototype_args(Prototype),
{function, Line, prototype_name_mangle(Prototype), length(Args), [function_clause(F)]}.

function_clause({function, Line, Prototype, Exprs, _Module}) ->
Args = egret_ast:prototype_args(Prototype),
{clause, Line, pattern_sequence(Args), [], body(Exprs)}.

pattern_sequence(Args) ->
lists:map(fun pattern/1, Args).

pattern({variable, Line, Arg}) ->
{var, Line, list_to_atom(Arg)}.

body(Exprs) ->
lists:map(fun expr/1, Exprs).

expr({real, Line, Number}) ->
{float, Line, Number}.

prototype_name_mangle({prototype, _Line, Name, _FormalArgs}) ->
list_to_atom(Name).
40 changes: 40 additions & 0 deletions src/egret_compiler.erl
@@ -0,0 +1,40 @@
%% @author Andrew J. Finnell
%% @copyright 2016 Andrew J. Finnell.

-module(egret_compiler).

-compile({parse_transform, do}).

%% Public API

-export([file/1, format_error/1]).

file(Filename) ->
do([error_m ||
Contents <- read_error(file:read_file(filename:absname(Filename)), Filename),
Tokens <- lexer_error(egret_lexer:string(binary_to_list(Contents)), Filename),
Toplevel <- parser_error(egret_parser:parse(Tokens), Filename),
IRModule <- egret_parser:toplevel_to_module(Toplevel, filename:rootname(filename:basename(Filename))),
AbsForms <- egret_codegen:module(IRModule),
egret_binarygen:forms(AbsForms)
]).

format_error(file_not_found) ->
"File not found".

%% Implementation

read_error({error, _Reason}, Filename) ->
{error, {[{Filename, [{none, ?MODULE, file_not_found}]}], []}};
read_error(Passthrough, _Filename) ->
Passthrough.

lexer_error({ok, Tokens, _EndLine}, _Filename) ->
{ok, Tokens};
lexer_error({error, ErrorInfo, _}, Filename) ->
{error, {[{Filename, [ErrorInfo]}], []}}.

parser_error({error, ErrorInfo}, Filename) ->
{error, {[{Filename, [ErrorInfo]}], []}};
parser_error(Passthrough, _Filename) ->
Passthrough.

0 comments on commit 63d315b

Please sign in to comment.