diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 54ed8f3..5968081 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -42,6 +42,7 @@ | {include, {file_system, [string()]} | {explicit_files, #{string() => binary()}}} | {src_file, string()} + | {src_dir, string()} | {aci, aeso_aci:aci_type()}. -type options() :: [option()]. @@ -87,7 +88,9 @@ file(Filename) -> file(File, Options0) -> Options = add_include_path(File, Options0), case read_contract(File) of - {ok, Bin} -> from_string(Bin, [{src_file, File} | Options]); + {ok, Bin} -> + SrcDir = aeso_utils:canonical_dir(filename:dirname(File)), + from_string(Bin, [{src_file, File}, {src_dir, SrcDir} | Options]); {error, Error} -> Msg = lists:flatten([File,": ",file:format_error(Error)]), {error, [aeso_errors:new(file_error, Msg)]} diff --git a/src/aeso_parse_lib.erl b/src/aeso_parse_lib.erl index 4c50527..8246ef6 100644 --- a/src/aeso_parse_lib.erl +++ b/src/aeso_parse_lib.erl @@ -15,7 +15,7 @@ many/1, many1/1, sep/2, sep1/2, infixl/2, infixr/2]). --export([current_file/0, set_current_file/1, +-export([current_file/0, set_current_file/1, current_dir/0, set_current_dir/1, current_include_type/0, set_current_include_type/1]). %% -- Types ------------------------------------------------------------------ @@ -480,6 +480,13 @@ current_file() -> set_current_file(File) -> put('$current_file', File). +%% Current source directory +current_dir() -> + get('$current_dir'). + +set_current_dir(File) -> + put('$current_dir', File). + add_current_file({L, C}) -> {current_file(), L, C}; add_current_file(Pos) -> Pos. diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 5f37cd9..3c03bf0 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -19,6 +19,7 @@ -include("aeso_parse_lib.hrl"). -import(aeso_parse_lib, [current_file/0, set_current_file/1, + current_dir/0, set_current_dir/1, current_include_type/0, set_current_include_type/1]). -type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none(). @@ -58,6 +59,7 @@ run_parser(P, Inp, Opts) -> parse_and_scan(P, S, Opts) -> set_current_file(proplists:get_value(src_file, Opts, no_file)), + set_current_dir(proplists:get_value(src_dir, Opts, no_file)), set_current_include_type(proplists:get_value(include_type, Opts, none)), case aeso_scan:scan(S) of {ok, Tokens} -> aeso_parse_lib:parse(P, Tokens); @@ -556,6 +558,7 @@ bracket_list(P) -> brackets(comma_sep(P)). -spec pos_ann(ann_line(), ann_col()) -> ann(). pos_ann(Line, Col) -> [ {file, current_file()} + , {dir, current_dir()} , {include_type, current_include_type()} , {line, Line} , {col, Col} ]. @@ -696,7 +699,7 @@ expand_includes([], Included, Acc, Opts) -> end; expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> case get_include_code(File, Ann, Opts) of - {ok, Code} -> + {ok, AbsDir, Code} -> Hashed = hash_include(File, Code), case sets:is_element(Hashed, Included) of false -> @@ -706,9 +709,10 @@ expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Op _ -> indirect end, Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), - Opts2 = lists:keystore(include_type, 1, Opts1, {include_type, IncludeType}), + Opts2 = lists:keystore(src_dir, 1, Opts1, {src_dir, AbsDir}), + Opts3 = lists:keystore(include_type, 1, Opts2, {include_type, IncludeType}), Included1 = sets:add_element(Hashed, Included), - case parse_and_scan(file(), Code, Opts2) of + case parse_and_scan(file(), Code, Opts3) of {ok, AST1} -> expand_includes(AST1 ++ AST, Included1, Acc, Opts); Err = {error, _} -> @@ -726,13 +730,12 @@ expand_includes([E | AST], Included, Acc, Opts) -> read_file(File, Opts) -> 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); + lists:foldr(fun(Path, {error, _}) -> read_file_(Path, File); + (_Path, OK) -> OK end, {error, not_found}, Paths); {explicit_files, Files} -> case maps:get(binary_to_list(File), Files, not_found) of not_found -> {error, not_found}; - Src -> {ok, Src} + Src -> {ok, File, Src} end; escript -> try @@ -741,7 +744,7 @@ read_file(File, Opts) -> Archive = proplists:get_value(archive, Sections), FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])), case zip:extract(Archive, [{file_list, [FileName]}, memory]) of - {ok, [{_, Src}]} -> {ok, Src}; + {ok, [{_, Src}]} -> {ok, escript, Src}; _ -> {error, not_found} end catch _:_ -> @@ -749,6 +752,13 @@ read_file(File, Opts) -> end end. +read_file_(Path, File) -> + AbsFile = filename:join(Path, File), + case file:read_file(AbsFile) of + {ok, Bin} -> {ok, aeso_utils:canonical_dir(filename:dirname(AbsFile)), Bin}; + Err -> Err + end. + stdlib_options() -> StdLibDir = aeso_stdlib:stdlib_include_path(), case filelib:is_dir(StdLibDir) of @@ -760,30 +770,29 @@ get_include_code(File, Ann, Opts) -> %% Temporarily extend include paths with the directory of the current file Opts1 = include_current_file_dir(Opts, Ann), case {read_file(File, Opts1), read_file(File, stdlib_options())} of - {{ok, Bin}, {ok, _}} -> + {{ok, Dir, Bin}, {ok, _}} -> case filename:basename(File) == File of true -> { error , fail( ann_pos(Ann) , "Illegal redefinition of standard library " ++ binary_to_list(File))}; %% If a path is provided then the stdlib takes lower priority - false -> {ok, binary_to_list(Bin)} + false -> {ok, Dir, binary_to_list(Bin)} end; - {_, {ok, Bin}} -> - {ok, binary_to_list(Bin)}; - {{ok, Bin}, _} -> - {ok, binary_to_list(Bin)}; + {_, {ok, _, Bin}} -> + {ok, stdlib, binary_to_list(Bin)}; + {{ok, Dir, Bin}, _} -> + {ok, Dir, binary_to_list(Bin)}; {_, _} -> {error, {ann_pos(Ann), include_error, File}} end. include_current_file_dir(Opts, Ann) -> - case {proplists:get_value(file, Ann, undefined), + case {proplists:get_value(dir, Ann, undefined), proplists:get_value(include, Opts, undefined)} of {undefined, _} -> Opts; - {FromFile, {file_system, Paths}} -> - BaseDir = aeso_utils:canonical_dir(filename:dirname(FromFile)), - case lists:member(BaseDir, Paths) of - false -> [{include, {file_system, [BaseDir | Paths]}} | Opts]; + {CurrDir, {file_system, Paths}} -> + case lists:member(CurrDir, Paths) of + false -> [{include, {file_system, [CurrDir | Paths]}} | Opts]; true -> Opts end; {_, _} -> Opts