From aa6d56ce9b72bace2d96810bc84e4681d9ea09f6 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 8 Feb 2019 14:05:29 +0100 Subject: [PATCH] Allow passing an explicit "file system" for included files to the compiler --- src/aeso_compiler.erl | 15 +++++++-------- src/aeso_parser.erl | 22 +++++++++++++--------- test/aeso_compiler_tests.erl | 28 ++++++++++++++++++---------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 6967a88..7fe0a6b 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -28,8 +28,8 @@ | pp_icode | pp_assembler | pp_bytecode - | {include_path, [string()]} - | {allow_include, boolean()} + | {include, {file_system, [string()]} | + {explicit_files, #{string() => binary()}}} | {src_file, string()}. -type options() :: [option()]. @@ -49,12 +49,13 @@ version() -> -spec file(string()) -> {ok, map()} | {error, binary()}. file(Filename) -> Dir = filename:dirname(Filename), - file(Filename, [{include_path, [Dir]}]). + {ok, Cwd} = file:get_cwd(), + file(Filename, [{include, {file_system, [Cwd, Dir]}}]). -spec file(string(), options()) -> {ok, map()} | {error, binary()}. file(File, Options) -> case read_contract(File) of - {ok, Bin} -> from_string(Bin, [{src_file, File}, {allow_include, true} | Options]); + {ok, Bin} -> from_string(Bin, [{src_file, File} | Options]); {error, Error} -> ErrorString = [File,": ",file:format_error(Error)], {error, join_errors("File errors", [ErrorString], fun(E) -> E end)} @@ -288,10 +289,8 @@ parse(Text, Options) -> ErrorString = io_lib:format("Ambiguous ~p", [As]), parse_error(Pos, ErrorString); %% Include error - {error, {Pos, include_not_allowed}} -> - parse_error(Pos, "includes not allowed in this context"); - {error, {Pos, include_error}} -> - parse_error(Pos, "could not find include file") + {error, {Pos, {include_error, File}}} -> + parse_error(Pos, io_lib:format("could not find include file '~s'", [File])) end. parse_error(Pos, ErrorString) -> diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index fc13e54..8b0a247 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -483,9 +483,8 @@ expand_includes(AST, Opts) -> expand_includes([], Acc, _Opts) -> {ok, lists:reverse(Acc)}; expand_includes([{include, S = {string, _, File}} | AST], Acc, Opts) -> - AllowInc = proplists:get_value(allow_include, Opts, false), case read_file(File, Opts) of - {ok, Bin} when AllowInc -> + {ok, Bin} -> Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), case string(binary_to_list(Bin), Opts1) of {ok, AST1} -> @@ -493,17 +492,22 @@ expand_includes([{include, S = {string, _, File}} | AST], Acc, Opts) -> Err = {error, _} -> Err end; - {ok, _} -> - {error, {get_pos(S), include_not_allowed}}; {error, _} -> - {error, {get_pos(S), include_error}} + {error, {get_pos(S), {include_error, File}}} end; expand_includes([E | AST], Acc, Opts) -> expand_includes(AST, [E | Acc], Opts). read_file(File, Opts) -> - CandidateNames = [File] ++ [ filename:join(Dir, File) - || Dir <- proplists:get_value(include_path, Opts, []) ], - lists:foldr(fun(F, {error, _}) -> file:read_file(F); - (_F, OK) -> OK end, {error, not_found}, CandidateNames). + case proplists:get_value(include, Opts, {explicit_files, #{}}) of + {file_system, Paths} -> + CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ], + lists:foldr(fun(F, {error, _}) -> file:read_file(F); + (_F, OK) -> OK end, {error, not_found}, CandidateNames); + {explicit_files, Files} -> + case maps:get(binary_to_list(File), Files, not_found) of + not_found -> {error, not_found}; + Src -> {ok, Src} + end + end. diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 23a760c..04fa19f 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -23,7 +23,7 @@ simple_compile_test_() -> end} || ContractName <- compilable_contracts() ] ++ [ {"Testing error messages of " ++ ContractName, fun() -> - case compile(ContractName, false) of + case compile(ContractName) of <<"Type errors\n", ErrorString/binary>> -> check_errors(lists:sort(ExpectedErrors), ErrorString); <<"Parse errors\n", ErrorString/binary>> -> @@ -31,6 +31,17 @@ simple_compile_test_() -> end end} || {ContractName, ExpectedErrors} <- failing_contracts() ] ++ + [ {"Testing include with explicit files", + fun() -> + FileSystem = maps:from_list( + [ begin + {ok, Bin} = file:read_file(filename:join([aeso_test_utils:contract_path(), File])), + {File, Bin} + end || File <- ["included.aes", "../contracts/included2.aes"] ]), + #{byte_code := Code1} = compile("include", [{include, {explicit_files, FileSystem}}]), + #{byte_code := Code2} = compile("include"), + ?assertMatch(true, Code1 == Code2) + end} ] ++ [ {"Testing deadcode elimination", fun() -> #{ byte_code := NoDeadCode } = compile("nodeadcode"), @@ -50,15 +61,14 @@ check_errors(Expect, ErrorString) -> {Missing, Extra} -> ?assertEqual(Missing, Extra) end. -compile(Name) -> compile(Name, true). +compile(Name) -> + compile(Name, [{include, {file_system, [aeso_test_utils:contract_path()]}}]). -compile(Name, AllowInc) -> +compile(Name, Options) -> String = aeso_test_utils:read_contract(Name), - case aeso_compiler:from_string(String, [{include_path, [aeso_test_utils:contract_path()]}, - {allow_include, AllowInc}, - {src_file, Name}]) of - {ok,Map} -> Map; - {error,ErrorString} -> ErrorString + case aeso_compiler:from_string(String, [{src_file, Name} | Options]) of + {ok, Map} -> Map; + {error, ErrorString} -> ErrorString end. %% compilable_contracts() -> [ContractName]. @@ -201,8 +211,6 @@ failing_contracts() -> " r.foo : (gas : int, value : int) => Remote.themap\n" "against the expected type\n" " (gas : int, value : int) => map(string, int)">>]} - , {"include", - [<<"file include, line 1, column 9: includes not allowed in this context\n">>]} , {"bad_include_and_ns", [<<"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>, <<"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]}