1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20-module(epp). 21 22%% An Erlang code preprocessor. 23 24-export([open/1,open/2,open/3,close/1,format_error/1]). 25-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]). 26-export([scan_file/1, scan_file/2, parse_file/1, parse_file/2, parse_file/3]). 27-export([default_encoding/0, encoding_to_string/1, 28 read_encoding_from_binary/1, read_encoding_from_binary/2, 29 set_encoding/1, set_encoding/2, read_encoding/1, read_encoding/2]). 30-export([interpret_file_attribute/1]). 31-export([normalize_typed_record_fields/1,restore_typed_record_fields/1]). 32 33%%------------------------------------------------------------------------ 34 35-export_type([source_encoding/0]). 36 37-type macros() :: [atom() | {atom(), term()} | {atom(), term(), 'redefine'}]. 38-type epp_handle() :: pid(). 39-type source_encoding() :: latin1 | utf8. 40 41-type ifdef() :: 'ifdef' | 'ifndef' | 'if' | 'else'. 42 43-type name() :: atom(). 44-type argspec() :: 'none' %No arguments 45 | non_neg_integer(). %Number of arguments 46-type argnames() :: [atom()]. 47-type tokens() :: [erl_scan:token()]. 48-type predef() :: 'undefined' | {'none', tokens()}. 49-type userdef() :: {argspec(), {argnames(), tokens()}}. 50-type used() :: {name(), argspec()}. 51 52-type function_name_type() :: 'undefined' 53 | {atom(),non_neg_integer()} 54 | tokens(). 55 56-type warning_info() :: {erl_anno:location(), module(), term()}. 57 58-define(DEFAULT_ENCODING, utf8). 59 60%% Epp state record. 61-record(epp, {file :: file:io_device() 62 | 'undefined', %Current file 63 location=1, %Current location 64 delta=0 :: non_neg_integer(), %Offset from Location (-file) 65 name="" :: file:name(), %Current file name 66 name2="" :: file:name(), %-"-, modified by -file 67 istk=[] :: [ifdef()], %Ifdef stack 68 sstk=[] :: [#epp{}], %State stack 69 path=[] :: [file:name()], %Include-path 70 macs = #{} %Macros (don't care locations) 71 :: #{name() => predef() | [userdef()]}, 72 uses = #{} %Macro use structure 73 :: #{name() => [{argspec(), [used()]}]}, 74 default_encoding = ?DEFAULT_ENCODING :: source_encoding(), 75 pre_opened = false :: boolean(), 76 fname = [] :: function_name_type() 77 }). 78 79%% open(Options) 80%% open(FileName, IncludePath) 81%% open(FileName, IncludePath, PreDefMacros) 82%% close(Epp) 83%% scan_erl_form(Epp) 84%% parse_erl_form(Epp) 85%% scan_file(Epp) 86%% scan_file(FileName, Options) 87%% parse_file(Epp) 88%% parse_file(FileName, Options) 89%% parse_file(FileName, IncludePath, PreDefMacros) 90%% macro_defs(Epp) 91 92-spec open(FileName, IncludePath) -> 93 {'ok', Epp} | {'error', ErrorDescriptor} when 94 FileName :: file:name(), 95 IncludePath :: [DirectoryName :: file:name()], 96 Epp :: epp_handle(), 97 ErrorDescriptor :: term(). 98 99open(Name, Path) -> 100 open(Name, Path, []). 101 102-spec open(FileName, IncludePath, PredefMacros) -> 103 {'ok', Epp} | {'error', ErrorDescriptor} when 104 FileName :: file:name(), 105 IncludePath :: [DirectoryName :: file:name()], 106 PredefMacros :: macros(), 107 Epp :: epp_handle(), 108 ErrorDescriptor :: term(). 109 110open(Name, Path, Pdm) -> 111 open([{name, Name}, {includes, Path}, {macros, Pdm}]). 112 113-spec open(Options) -> 114 {'ok', Epp} | {'ok', Epp, Extra} | {'error', ErrorDescriptor} when 115 Options :: [{'default_encoding', DefEncoding :: source_encoding()} | 116 {'includes', IncludePath :: [DirectoryName :: file:name()]} | 117 {'source_name', SourceName :: file:name()} | 118 {'macros', PredefMacros :: macros()} | 119 {'name',FileName :: file:name()} | 120 {'location',StartLocation :: erl_anno:location()} | 121 {'fd',FileDescriptor :: file:io_device()} | 122 'extra'], 123 Epp :: epp_handle(), 124 Extra :: [{'encoding', source_encoding() | 'none'}], 125 ErrorDescriptor :: term(). 126 127open(Options) -> 128 case proplists:get_value(name, Options) of 129 undefined -> 130 erlang:error(badarg); 131 Name -> 132 Self = self(), 133 Epp = spawn(fun() -> server(Self, Name, Options) end), 134 case epp_request(Epp) of 135 {ok, Pid, Encoding} -> 136 case proplists:get_bool(extra, Options) of 137 true -> {ok, Pid, [{encoding, Encoding}]}; 138 false -> {ok, Pid} 139 end; 140 Other -> 141 Other 142 end 143 end. 144 145-spec close(Epp) -> 'ok' when 146 Epp :: epp_handle(). 147 148close(Epp) -> 149 %% Make sure that close is synchronous as a courtesy to test 150 %% cases that test for resource leaks. 151 Ref = erlang:monitor(process, Epp), 152 R = epp_request(Epp, close), 153 receive {'DOWN',Ref,_,_,_} -> ok end, 154 R. 155 156-spec scan_erl_form(Epp) -> 157 {'ok', Tokens} | {error, ErrorInfo} | 158 {'warning',WarningInfo} | {'eof',Line} when 159 Epp :: epp_handle(), 160 Tokens :: erl_scan:tokens(), 161 Line :: erl_anno:line(), 162 ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), 163 WarningInfo :: warning_info(). 164 165scan_erl_form(Epp) -> 166 epp_request(Epp, scan_erl_form). 167 168-spec parse_erl_form(Epp) -> 169 {'ok', AbsForm} | {error, ErrorInfo} | 170 {'warning',WarningInfo} | {'eof',Location} when 171 Epp :: epp_handle(), 172 AbsForm :: erl_parse:abstract_form(), 173 Location :: erl_anno:location(), 174 ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), 175 WarningInfo :: warning_info(). 176 177parse_erl_form(Epp) -> 178 case epp_request(Epp, scan_erl_form) of 179 {ok,Toks} -> 180 erl_parse:parse_form(Toks); 181 Other -> 182 Other 183 end. 184 185macro_defs(Epp) -> 186 epp_request(Epp, macro_defs). 187 188%% format_error(ErrorDescriptor) -> String 189%% Return a string describing the error. 190 191-spec format_error(ErrorDescriptor) -> io_lib:chars() when 192 ErrorDescriptor :: term(). 193 194format_error(cannot_parse) -> 195 io_lib:format("cannot parse file, giving up", []); 196format_error({bad,W}) -> 197 io_lib:format("badly formed '~s'", [W]); 198format_error({duplicated_argument, Arg}) -> 199 io_lib:format("argument '~ts' already used", [Arg]); 200format_error(missing_parenthesis) -> 201 io_lib:format("badly formed define: missing closing right parenthesis",[]); 202format_error(missing_comma) -> 203 io_lib:format("badly formed define: missing comma",[]); 204format_error(premature_end) -> 205 "premature end"; 206format_error({call,What}) -> 207 io_lib:format("illegal macro call '~ts'",[What]); 208format_error({undefined,M,none}) -> 209 io_lib:format("undefined macro '~ts'", [M]); 210format_error({undefined,M,A}) -> 211 io_lib:format("undefined macro '~ts/~p'", [M,A]); 212format_error({depth,What}) -> 213 io_lib:format("~s too deep",[What]); 214format_error({mismatch,M}) -> 215 io_lib:format("argument mismatch for macro '~ts'", [M]); 216format_error({arg_error,M}) -> 217 io_lib:format("badly formed argument for macro '~ts'", [M]); 218format_error({redefine,M}) -> 219 io_lib:format("redefining macro '~ts'", [M]); 220format_error({redefine_predef,M}) -> 221 io_lib:format("redefining predefined macro '~s'", [M]); 222format_error({circular,M,none}) -> 223 io_lib:format("circular macro '~ts'", [M]); 224format_error({circular,M,A}) -> 225 io_lib:format("circular macro '~ts/~p'", [M,A]); 226format_error({include,W,F}) -> 227 io_lib:format("can't find include ~s \"~ts\"", [W,F]); 228format_error({illegal,How,What}) -> 229 io_lib:format("~s '-~s'", [How,What]); 230format_error({illegal_function,Macro}) -> 231 io_lib:format("?~s can only be used within a function", [Macro]); 232format_error({illegal_function_usage,Macro}) -> 233 io_lib:format("?~s must not begin a form", [Macro]); 234format_error(elif_after_else) -> 235 "'elif' following 'else'"; 236format_error({'NYI',What}) -> 237 io_lib:format("not yet implemented '~s'", [What]); 238format_error({error,Term}) -> 239 io_lib:format("-error(~tp).", [Term]); 240format_error({warning,Term}) -> 241 io_lib:format("-warning(~tp).", [Term]); 242format_error(E) -> file:format_error(E). 243 244-spec scan_file(FileName, Options) -> 245 {'ok', [Form], Extra} | {error, OpenError} when 246 FileName :: file:name(), 247 Options :: [{'includes', IncludePath :: [DirectoryName :: file:name()]} | 248 {'source_name', SourceName :: file:name()} | 249 {'macros', PredefMacros :: macros()} | 250 {'default_encoding', DefEncoding :: source_encoding()}], 251 Form :: erl_scan:tokens() | {'error', ErrorInfo} | {'eof', Loc}, 252 Loc :: erl_anno:location(), 253 ErrorInfo :: erl_scan:error_info(), 254 Extra :: [{'encoding', source_encoding() | 'none'}], 255 OpenError :: file:posix() | badarg | system_limit. 256 257scan_file(Ifile, Options) -> 258 case open([{name, Ifile}, extra | Options]) of 259 {ok,Epp,Extra} -> 260 Forms = scan_file(Epp), 261 close(Epp), 262 {ok,Forms,Extra}; 263 {error,E} -> 264 {error,E} 265 end. 266 267scan_file(Epp) -> 268 case scan_erl_form(Epp) of 269 {ok,Toks} -> 270 [Toks|scan_file(Epp)]; 271 {error,E} -> 272 [{error,E}|scan_file(Epp)]; 273 {eof,Location} -> 274 [{eof,Location}] 275 end. 276 277-spec parse_file(FileName, IncludePath, PredefMacros) -> 278 {'ok', [Form]} | {error, OpenError} when 279 FileName :: file:name(), 280 IncludePath :: [DirectoryName :: file:name()], 281 Form :: erl_parse:abstract_form() 282 | {'error', ErrorInfo} 283 | {'eof',Location}, 284 PredefMacros :: macros(), 285 Location :: erl_anno:location(), 286 ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), 287 OpenError :: file:posix() | badarg | system_limit. 288 289parse_file(Ifile, Path, Predefs) -> 290 parse_file(Ifile, [{includes, Path}, {macros, Predefs}]). 291 292-spec parse_file(FileName, Options) -> 293 {'ok', [Form]} | {'ok', [Form], Extra} | {error, OpenError} when 294 FileName :: file:name(), 295 Options :: [{'includes', IncludePath :: [DirectoryName :: file:name()]} | 296 {'source_name', SourceName :: file:name()} | 297 {'macros', PredefMacros :: macros()} | 298 {'default_encoding', DefEncoding :: source_encoding()} | 299 {'location',StartLocation :: erl_anno:location()} | 300 'extra'], 301 Form :: erl_parse:abstract_form() 302 | {'error', ErrorInfo} 303 | {'eof',Location}, 304 Location :: erl_anno:location(), 305 ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), 306 Extra :: [{'encoding', source_encoding() | 'none'}], 307 OpenError :: file:posix() | badarg | system_limit. 308 309parse_file(Ifile, Options) -> 310 case open([{name, Ifile} | Options]) of 311 {ok,Epp} -> 312 Forms = parse_file(Epp), 313 close(Epp), 314 {ok,Forms}; 315 {ok,Epp,Extra} -> 316 Forms = parse_file(Epp), 317 close(Epp), 318 {ok,Forms,Extra}; 319 {error,E} -> 320 {error,E} 321 end. 322 323-spec parse_file(Epp) -> [Form] when 324 Epp :: epp_handle(), 325 Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | 326 {'warning',WarningInfo} | {'eof',Location}, 327 Location :: erl_anno:location(), 328 ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), 329 WarningInfo :: warning_info(). 330 331parse_file(Epp) -> 332 case parse_erl_form(Epp) of 333 {ok,Form} -> 334 [Form|parse_file(Epp)]; 335 {error,E} -> 336 [{error,E}|parse_file(Epp)]; 337 {warning,W} -> 338 [{warning,W}|parse_file(Epp)]; 339 {eof,Location} -> 340 [{eof,Location}] 341 end. 342 343-spec default_encoding() -> source_encoding(). 344 345default_encoding() -> 346 ?DEFAULT_ENCODING. 347 348-spec encoding_to_string(Encoding) -> string() when 349 Encoding :: source_encoding(). 350 351encoding_to_string(latin1) -> "coding: latin-1"; 352encoding_to_string(utf8) -> "coding: utf-8". 353 354-spec read_encoding(FileName) -> source_encoding() | none when 355 FileName :: file:name(). 356 357read_encoding(Name) -> 358 read_encoding(Name, []). 359 360-spec read_encoding(FileName, Options) -> source_encoding() | none when 361 FileName :: file:name(), 362 Options :: [Option], 363 Option :: {in_comment_only, boolean()}. 364 365read_encoding(Name, Options) -> 366 InComment = proplists:get_value(in_comment_only, Options, true), 367 case file:open(Name, [read]) of 368 {ok,File} -> 369 try read_encoding_from_file(File, InComment) 370 after ok = file:close(File) 371 end; 372 _Error -> 373 none 374 end. 375 376-spec set_encoding(File) -> source_encoding() | none when 377 File :: io:device(). % pid(); raw files don't work 378 379set_encoding(File) -> 380 set_encoding(File, ?DEFAULT_ENCODING). 381 382-spec set_encoding(File, Default) -> source_encoding() | none when 383 Default :: source_encoding(), 384 File :: io:device(). % pid(); raw files don't work 385 386set_encoding(File, Default) -> 387 Encoding = read_encoding_from_file(File, true), 388 Enc = case Encoding of 389 none -> Default; 390 Encoding -> Encoding 391 end, 392 ok = io:setopts(File, [{encoding, Enc}]), 393 Encoding. 394 395-spec read_encoding_from_binary(Binary) -> source_encoding() | none when 396 Binary :: binary(). 397 398-define(ENC_CHUNK, 32). 399-define(N_ENC_CHUNK, 16). % a total of 512 bytes 400 401read_encoding_from_binary(Binary) -> 402 read_encoding_from_binary(Binary, []). 403 404-spec read_encoding_from_binary(Binary, Options) -> 405 source_encoding() | none when 406 Binary :: binary(), 407 Options :: [Option], 408 Option :: {in_comment_only, boolean()}. 409 410read_encoding_from_binary(Binary, Options) -> 411 InComment = proplists:get_value(in_comment_only, Options, true), 412 try 413 com_nl(Binary, fake_reader(0), 0, InComment) 414 catch 415 throw:no -> 416 none 417 end. 418 419fake_reader(N) -> 420 fun() when N =:= ?N_ENC_CHUNK -> 421 throw(no); 422 () -> 423 {<<>>, fake_reader(N+1)} 424 end. 425 426-spec read_encoding_from_file(File, InComment) -> source_encoding() | none when 427 File :: io:device(), 428 InComment :: boolean(). 429 430read_encoding_from_file(File, InComment) -> 431 {ok, Pos0} = file:position(File, cur), 432 Opts = io:getopts(File), 433 Encoding0 = lists:keyfind(encoding, 1, Opts), 434 Binary0 = lists:keyfind(binary, 1, Opts), 435 ok = io:setopts(File, [binary, {encoding, latin1}]), 436 try 437 {B, Fun} = (reader(File, 0))(), 438 com_nl(B, Fun, 0, InComment) 439 catch 440 throw:no -> 441 none 442 after 443 {ok, Pos0} = file:position(File, Pos0), 444 ok = io:setopts(File, [Binary0, Encoding0]) 445 end. 446 447reader(Fd, N) -> 448 fun() when N =:= ?N_ENC_CHUNK -> 449 throw(no); 450 () -> 451 case file:read(Fd, ?ENC_CHUNK) of 452 eof -> 453 {<<>>, reader(Fd, N+1)}; 454 {ok, Bin} -> 455 {Bin, reader(Fd, N+1)}; 456 {error, _} -> 457 throw(no) % ignore errors 458 end 459 end. 460 461com_nl(_, _, 2, _) -> 462 throw(no); 463com_nl(B, Fun, N, false=Com) -> 464 com_c(B, Fun, N, Com); 465com_nl(B, Fun, N, true=Com) -> 466 com(B, Fun, N, Com). 467 468com(<<"\n",B/binary>>, Fun, N, Com) -> 469 com_nl(B, Fun, N+1, Com); 470com(<<"%", B/binary>>, Fun, N, Com) -> 471 com_c(B, Fun, N, Com); 472com(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> 473 com(B, Fun, N, Com); 474com(<<>>, Fun, N, Com) -> 475 {B, Fun1} = Fun(), 476 com(B, Fun1, N, Com). 477 478com_c(<<"c",B/binary>>, Fun, N, Com) -> 479 com_oding(B, Fun, N, Com); 480com_c(<<"\n",B/binary>>, Fun, N, Com) -> 481 com_nl(B, Fun, N+1, Com); 482com_c(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> 483 com_c(B, Fun, N, Com); 484com_c(<<>>, Fun, N, Com) -> 485 {B, Fun1} = Fun(), 486 com_c(B, Fun1, N, Com). 487 488com_oding(<<"oding",B/binary>>, Fun, N, Com) -> 489 com_sep(B, Fun, N, Com); 490com_oding(B, Fun, N, Com) when byte_size(B) >= length("oding") -> 491 com_c(B, Fun, N, Com); 492com_oding(B, Fun, N, Com) -> 493 {B1, Fun1} = Fun(), 494 com_oding(list_to_binary([B, B1]), Fun1, N, Com). 495 496com_sep(<<":",B/binary>>, Fun, N, Com) -> 497 com_space(B, Fun, N, Com); 498com_sep(<<"=",B/binary>>, Fun, N, Com) -> 499 com_space(B, Fun, N, Com); 500com_sep(<<"\s",B/binary>>, Fun, N, Com) -> 501 com_sep(B, Fun, N, Com); 502com_sep(<<>>, Fun, N, Com) -> 503 {B, Fun1} = Fun(), 504 com_sep(B, Fun1, N, Com); 505com_sep(B, Fun, N, Com) -> 506 com_c(B, Fun, N, Com). 507 508com_space(<<"\s",B/binary>>, Fun, N, Com) -> 509 com_space(B, Fun, N, Com); 510com_space(<<>>, Fun, N, Com) -> 511 {B, Fun1} = Fun(), 512 com_space(B, Fun1, N, Com); 513com_space(B, Fun, N, _Com) -> 514 com_enc(B, Fun, N, [], []). 515 516com_enc(<<C:1/unit:8,B/binary>>, Fun, N, L, Ps) when C >= $a, C =< $z; 517 C >= $A, C =< $Z; 518 C >= $0, C =< $9 -> 519 com_enc(B, Fun, N, [C | L], Ps); 520com_enc(<<>>, Fun, N, L, Ps) -> 521 case Fun() of 522 {<<>>, _} -> 523 com_enc_end([L | Ps]); 524 {B, Fun1} -> 525 com_enc(B, Fun1, N, L, Ps) 526 end; 527com_enc(<<"-",B/binary>>, Fun, N, L, Ps) -> 528 com_enc(B, Fun, N, [], [L | Ps]); 529com_enc(_B, _Fun, _N, L, Ps) -> 530 com_enc_end([L | Ps]). 531 532com_enc_end(Ps0) -> 533 Ps = lists:reverse([lists:reverse(lowercase(P)) || P <- Ps0]), 534 com_encoding(Ps). 535 536com_encoding(["latin","1"|_]) -> 537 latin1; 538com_encoding(["utf","8"|_]) -> 539 utf8; 540com_encoding(_) -> 541 throw(no). % Don't try any further 542 543lowercase(S) -> 544 unicode:characters_to_list(string:lowercase(S)). 545 546normalize_typed_record_fields([]) -> 547 {typed, []}; 548normalize_typed_record_fields(Fields) -> 549 normalize_typed_record_fields(Fields, [], false). 550 551normalize_typed_record_fields([], NewFields, Typed) -> 552 case Typed of 553 true -> {typed, lists:reverse(NewFields)}; 554 false -> not_typed 555 end; 556normalize_typed_record_fields([{typed_record_field,Field,_}|Rest], 557 NewFields, _Typed) -> 558 normalize_typed_record_fields(Rest, [Field|NewFields], true); 559normalize_typed_record_fields([Field|Rest], NewFields, Typed) -> 560 normalize_typed_record_fields(Rest, [Field|NewFields], Typed). 561 562restore_typed_record_fields([]) -> 563 []; 564restore_typed_record_fields([{attribute,A,record,{Record,_NewFields}}, 565 {attribute,A,type,{{record,Record},Fields,[]}}| 566 Forms]) -> 567 [{attribute,A,record,{Record,Fields}}| 568 restore_typed_record_fields(Forms)]; 569restore_typed_record_fields([{attribute,A,type,{{record,Record},Fields,[]}}| 570 Forms]) -> 571 %% This clause is due to the compiler's 'E' option. 572 %% Record information kept by erl_expand_records. 573 [{attribute,A,record,{Record,Fields}}| 574 restore_typed_record_fields(Forms)]; 575restore_typed_record_fields([Form|Forms]) -> 576 [Form|restore_typed_record_fields(Forms)]. 577 578server(Pid, Name, Options) -> 579 process_flag(trap_exit, true), 580 St = #epp{}, 581 case proplists:get_value(fd, Options) of 582 undefined -> 583 case file:open(Name, [read]) of 584 {ok,File} -> 585 init_server(Pid, Name, Options, St#epp{file = File}); 586 {error,E} -> 587 epp_reply(Pid, {error,E}) 588 end; 589 Fd -> 590 init_server(Pid, Name, Options, St#epp{file = Fd, pre_opened=true}) 591 end. 592 593init_server(Pid, FileName, Options, St0) -> 594 SourceName = proplists:get_value(source_name, Options, FileName), 595 Pdm = proplists:get_value(macros, Options, []), 596 Ms0 = predef_macros(SourceName), 597 case user_predef(Pdm, Ms0) of 598 {ok,Ms1} -> 599 DefEncoding = proplists:get_value(default_encoding, Options, 600 ?DEFAULT_ENCODING), 601 Encoding = set_encoding(St0#epp.file, DefEncoding), 602 epp_reply(Pid, {ok,self(),Encoding}), 603 %% ensure directory of current source file is 604 %% first in path 605 Path = [filename:dirname(FileName) | 606 proplists:get_value(includes, Options, [])], 607 %% the default location is 1 for backwards compatibility, not {1,1} 608 AtLocation = proplists:get_value(location, Options, 1), 609 St = St0#epp{delta=0, name=SourceName, name2=SourceName, 610 path=Path, location=AtLocation, macs=Ms1, 611 default_encoding=DefEncoding}, 612 From = wait_request(St), 613 Anno = erl_anno:new(AtLocation), 614 enter_file_reply(From, file_name(SourceName), Anno, 615 AtLocation, code), 616 wait_req_scan(St); 617 {error,E} -> 618 epp_reply(Pid, {error,E}) 619 end. 620 621%% predef_macros(FileName) -> Macrodict 622%% Initialise the macro dictionary with the default predefined macros, 623%% FILE, LINE, MODULE as undefined, MACHINE and MACHINE value. 624 625predef_macros(File) -> 626 Machine = list_to_atom(erlang:system_info(machine)), 627 Anno = line1(), 628 OtpVersion = list_to_integer(erlang:system_info(otp_release)), 629 Defs = [{'FILE', {none,[{string,Anno,File}]}}, 630 {'FUNCTION_NAME', undefined}, 631 {'FUNCTION_ARITY', undefined}, 632 {'LINE', {none,[{integer,Anno,1}]}}, 633 {'MODULE', undefined}, 634 {'MODULE_STRING', undefined}, 635 {'BASE_MODULE', undefined}, 636 {'BASE_MODULE_STRING', undefined}, 637 {'MACHINE', {none,[{atom,Anno,Machine}]}}, 638 {Machine, {none,[{atom,Anno,true}]}}, 639 {'OTP_RELEASE', {none,[{integer,Anno,OtpVersion}]}} 640 ], 641 maps:from_list(Defs). 642 643%% user_predef(PreDefMacros, Macros) -> 644%% {ok,MacroDict} | {error,E} 645%% Add the predefined macros to the macros dictionary. A macro without a 646%% value gets the value 'true'. 647 648user_predef([{M,Val,redefine}|Pdm], Ms) when is_atom(M) -> 649 Exp = erl_parse:tokens(erl_parse:abstract(Val)), 650 user_predef(Pdm, Ms#{M=>{none,Exp}}); 651user_predef([{M,Val}|Pdm], Ms) when is_atom(M) -> 652 case Ms of 653 #{M:=Defs} when is_list(Defs) -> 654 %% User defined macros. 655 {error,{redefine,M}}; 656 #{M:=_Defs} -> 657 %% Predefined macros. 658 {error,{redefine_predef,M}}; 659 _ -> 660 Exp = erl_parse:tokens(erl_parse:abstract(Val)), 661 user_predef(Pdm, Ms#{M=>[{none,{none,Exp}}]}) 662 end; 663user_predef([M|Pdm], Ms) when is_atom(M) -> 664 user_predef([{M,true}|Pdm], Ms); 665user_predef([Md|_Pdm], _Ms) -> {error,{bad,Md}}; 666user_predef([], Ms) -> {ok,Ms}. 667 668%% wait_request(EppState) -> RequestFrom 669%% wait_req_scan(EppState) 670%% wait_req_skip(EppState, SkipIstack) 671%% Handle requests, processing trivial requests directly. Either return 672%% requestor or scan/skip tokens. 673 674wait_request(St) -> 675 receive 676 {epp_request,From,scan_erl_form} -> From; 677 {epp_request,From,macro_defs} -> 678 %% Return the old format to avoid any incompability issues. 679 Defs = [{{atom,K},V} || {K,V} <- maps:to_list(St#epp.macs)], 680 epp_reply(From, Defs), 681 wait_request(St); 682 {epp_request,From,close} -> 683 close_file(St), 684 epp_reply(From, ok), 685 exit(normal); 686 {'EXIT',_,R} -> 687 exit(R); 688 Other -> 689 io:fwrite("Epp: unknown '~w'\n", [Other]), 690 wait_request(St) 691 end. 692 693close_file(#epp{pre_opened = true}) -> 694 ok; 695close_file(#epp{pre_opened = false, file = File}) -> 696 ok = file:close(File). 697 698wait_req_scan(St) -> 699 From = wait_request(St), 700 scan_toks(From, St). 701 702wait_req_skip(St, Sis) -> 703 From = wait_request(St), 704 skip_toks(From, St, Sis). 705 706%% enter_file(FileName, IncludeToken, From, EppState) 707%% leave_file(From, EppState) 708%% Handle entering and leaving included files. Notify caller when the 709%% current file is changed. Note it is an error to exit a file if we are 710%% in a conditional. These functions never return. 711 712enter_file(_NewName, Inc, From, St) 713 when length(St#epp.sstk) >= 8 -> 714 epp_reply(From, {error,{loc(Inc),epp,{depth,"include"}}}), 715 wait_req_scan(St); 716enter_file(NewName, Inc, From, St) -> 717 case file:path_open(St#epp.path, NewName, [read]) of 718 {ok,NewF,Pname} -> 719 Loc = start_loc(St#epp.location), 720 wait_req_scan(enter_file2(NewF, Pname, From, St, Loc)); 721 {error,_E} -> 722 epp_reply(From, {error,{loc(Inc),epp,{include,file,NewName}}}), 723 wait_req_scan(St) 724 end. 725 726%% enter_file2(File, FullName, From, EppState, AtLocation) -> EppState. 727%% Set epp to use this file and "enter" it. 728 729enter_file2(NewF, Pname, From, St0, AtLocation) -> 730 Anno = erl_anno:new(AtLocation), 731 enter_file_reply(From, Pname, Anno, AtLocation, code), 732 #epp{macs = Ms0, 733 default_encoding = DefEncoding} = St0, 734 Ms = Ms0#{'FILE':={none,[{string,Anno,Pname}]}}, 735 %% update the head of the include path to be the directory of the new 736 %% source file, so that an included file can always include other files 737 %% relative to its current location (this is also how C does it); note 738 %% that the directory of the parent source file (the previous head of 739 %% the path) must be dropped, otherwise the path used within the current 740 %% file will depend on the order of file inclusions in the parent files 741 Path = [filename:dirname(Pname) | tl(St0#epp.path)], 742 _ = set_encoding(NewF, DefEncoding), 743 #epp{file=NewF,location=AtLocation,name=Pname,name2=Pname,delta=0, 744 sstk=[St0|St0#epp.sstk],path=Path,macs=Ms, 745 default_encoding=DefEncoding}. 746 747enter_file_reply(From, Name, LocationAnno, AtLocation, Where) -> 748 Anno0 = erl_anno:new(AtLocation), 749 Anno = case Where of 750 code -> Anno0; 751 generated -> erl_anno:set_generated(true, Anno0) 752 end, 753 Rep = {ok, [{'-',Anno},{atom,Anno,file},{'(',Anno}, 754 {string,Anno,Name},{',',Anno}, 755 {integer,Anno,get_line(LocationAnno)},{')',LocationAnno}, 756 {dot,Anno}]}, 757 epp_reply(From, Rep). 758 759%% Flatten filename to a string. Must be a valid filename. 760 761file_name([C | T]) when is_integer(C), C > 0 -> 762 [C | file_name(T)]; 763file_name([H|T]) -> 764 file_name(H) ++ file_name(T); 765file_name([]) -> 766 []; 767file_name(N) when is_atom(N) -> 768 atom_to_list(N). 769 770leave_file(From, St) -> 771 case St#epp.istk of 772 [I|Cis] -> 773 epp_reply(From, 774 {error,{St#epp.location,epp, 775 {illegal,"unterminated",I}}}), 776 leave_file(wait_request(St),St#epp{istk=Cis}); 777 [] -> 778 case St#epp.sstk of 779 [OldSt|Sts] -> 780 close_file(St), 781 #epp{location=OldLoc, delta=Delta, name=OldName, 782 name2=OldName2} = OldSt, 783 CurrLoc = add_line(OldLoc, Delta), 784 Anno = erl_anno:new(CurrLoc), 785 Ms0 = St#epp.macs, 786 Ms = Ms0#{'FILE':={none,[{string,Anno,OldName2}]}}, 787 NextSt = OldSt#epp{sstk=Sts,macs=Ms,uses=St#epp.uses}, 788 enter_file_reply(From, OldName, Anno, CurrLoc, code), 789 case OldName2 =:= OldName of 790 true -> 791 ok; 792 false -> 793 NFrom = wait_request(NextSt), 794 OldAnno = erl_anno:new(OldLoc), 795 enter_file_reply(NFrom, OldName2, OldAnno, 796 CurrLoc, generated) 797 end, 798 wait_req_scan(NextSt); 799 [] -> 800 epp_reply(From, {eof,St#epp.location}), 801 wait_req_scan(St) 802 end 803 end. 804 805%% scan_toks(From, EppState) 806%% scan_toks(Tokens, From, EppState) 807 808scan_toks(From, St) -> 809 case io:scan_erl_form(St#epp.file, '', St#epp.location) of 810 {ok,Toks,Cl} -> 811 scan_toks(Toks, From, St#epp{location=Cl}); 812 {error,E,Cl} -> 813 epp_reply(From, {error,E}), 814 wait_req_scan(St#epp{location=Cl}); 815 {eof,Cl} -> 816 leave_file(From, St#epp{location=Cl}); 817 {error,_E} -> 818 epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}), 819 leave_file(wait_request(St), St) %This serious, just exit! 820 end. 821 822scan_toks([{'-',_Lh},{atom,_Ld,define}=Define|Toks], From, St) -> 823 scan_define(Toks, Define, From, St); 824scan_toks([{'-',_Lh},{atom,_Ld,undef}=Undef|Toks], From, St) -> 825 scan_undef(Toks, Undef, From, St); 826scan_toks([{'-',_Lh},{atom,_Ld,error}=Error|Toks], From, St) -> 827 scan_err_warn(Toks, Error, From, St); 828scan_toks([{'-',_Lh},{atom,_Ld,warning}=Warn|Toks], From, St) -> 829 scan_err_warn(Toks, Warn, From, St); 830scan_toks([{'-',_Lh},{atom,_Li,include}=Inc|Toks], From, St) -> 831 scan_include(Toks, Inc, From, St); 832scan_toks([{'-',_Lh},{atom,_Li,include_lib}=IncLib|Toks], From, St) -> 833 scan_include_lib(Toks, IncLib, From, St); 834scan_toks([{'-',_Lh},{atom,_Li,ifdef}=IfDef|Toks], From, St) -> 835 scan_ifdef(Toks, IfDef, From, St); 836scan_toks([{'-',_Lh},{atom,_Li,ifndef}=IfnDef|Toks], From, St) -> 837 scan_ifndef(Toks, IfnDef, From, St); 838scan_toks([{'-',_Lh},{atom,_Le,'else'}=Else|Toks], From, St) -> 839 scan_else(Toks, Else, From, St); 840scan_toks([{'-',_Lh},{'if',_Le}=If|Toks], From, St) -> 841 scan_if(Toks, If, From, St); 842scan_toks([{'-',_Lh},{atom,_Le,elif}=Elif|Toks], From, St) -> 843 scan_elif(Toks, Elif, From, St); 844scan_toks([{'-',_Lh},{atom,_Le,endif}=Endif|Toks], From, St) -> 845 scan_endif(Toks, Endif, From, St); 846scan_toks([{'-',_Lh},{atom,_Lf,file}=FileToken|Toks0], From, St) -> 847 case catch expand_macros(Toks0, St) of 848 Toks1 when is_list(Toks1) -> 849 scan_file(Toks1, FileToken, From, St); 850 {error,ErrL,What} -> 851 epp_reply(From, {error,{ErrL,epp,What}}), 852 wait_req_scan(St) 853 end; 854scan_toks(Toks0, From, St) -> 855 case catch expand_macros(Toks0, St#epp{fname=Toks0}) of 856 Toks1 when is_list(Toks1) -> 857 epp_reply(From, {ok,Toks1}), 858 wait_req_scan(St#epp{macs=scan_module(Toks1, St#epp.macs)}); 859 {error,ErrL,What} -> 860 epp_reply(From, {error,{ErrL,epp,What}}), 861 wait_req_scan(St) 862 end. 863 864scan_module([{'-',_Ah},{atom,_Am,module},{'(',_Al}|Ts], Ms) -> 865 scan_module_1(Ts, Ms); 866scan_module([{'-',_Ah},{atom,_Am,extends},{'(',_Al}|Ts], Ms) -> 867 scan_extends(Ts, Ms); 868scan_module(_Ts, Ms) -> Ms. 869 870scan_module_1([{atom,_,_}=A,{',',Anno}|Ts], Ms) -> 871 %% Parameterized modules. 872 scan_module_1([A,{')',Anno}|Ts], Ms); 873scan_module_1([{atom,Anno,A}=ModAtom,{')',_Ar}|_Ts], Ms0) -> 874 ModString = atom_to_list(A), 875 Ms = Ms0#{'MODULE':={none,[ModAtom]}}, 876 Ms#{'MODULE_STRING':={none,[{string,Anno,ModString}]}}; 877scan_module_1(_Ts, Ms) -> Ms. 878 879scan_extends([{atom,Anno,A}=ModAtom,{')',_Ar}|_Ts], Ms0) -> 880 ModString = atom_to_list(A), 881 Ms = Ms0#{'BASE_MODULE':={none,[ModAtom]}}, 882 Ms#{'BASE_MODULE_STRING':={none,[{string,Anno,ModString}]}}; 883scan_extends(_Ts, Ms) -> Ms. 884 885scan_err_warn([{'(',_}|_]=Toks0, {atom,_,Tag}=Token, From, St) -> 886 try expand_macros(Toks0, St) of 887 Toks when is_list(Toks) -> 888 case erl_parse:parse_term(Toks) of 889 {ok,Term} -> 890 epp_reply(From, {Tag,{loc(Token),epp,{Tag,Term}}}); 891 {error,_} -> 892 epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}}) 893 end 894 catch 895 _:_ -> 896 epp_reply(From, {error,{loc(Token),epp,{bad,Tag}}}) 897 end, 898 wait_req_scan(St); 899scan_err_warn(Toks, {atom,_,Tag}=Token, From, St) -> 900 T = no_match(Toks, Token), 901 epp_reply(From, {error,{loc(T),epp,{bad,Tag}}}), 902 wait_req_scan(St). 903 904%% scan_define(Tokens, DefineToken, From, EppState) 905 906scan_define([{'(',_Ap},{Type,_Am,_}=Mac|Toks], Def, From, St) 907 when Type =:= atom; Type =:= var -> 908 scan_define_1(Toks, Mac, Def, From, St); 909scan_define(Toks, Def, From, St) -> 910 T = find_mismatch(['(', var_or_atom], Toks, Def), 911 epp_reply(From, {error,{loc(T),epp,{bad,define}}}), 912 wait_req_scan(St). 913 914scan_define_1([{',',_}=Comma|Toks], Mac,_Def, From, St) -> 915 case catch macro_expansion(Toks, Comma) of 916 Expansion when is_list(Expansion) -> 917 scan_define_2(none, {none,Expansion}, Mac, From, St); 918 {error,ErrL,What} -> 919 epp_reply(From, {error,{ErrL,epp,What}}), 920 wait_req_scan(St) 921 end; 922scan_define_1([{'(',_Ac}=T|Toks], Mac, _Def, From, St) -> 923 case catch macro_pars(Toks, [], T) of 924 {ok,{As,_}=MacroDef} -> 925 Len = length(As), 926 scan_define_2(Len, MacroDef, Mac, From, St); 927 {error,ErrL,What} -> 928 epp_reply(From, {error,{ErrL,epp,What}}), 929 wait_req_scan(St) 930 end; 931scan_define_1(Toks, _Mac, Def, From, St) -> 932 T = no_match(Toks, Def), 933 epp_reply(From, {error,{loc(T),epp,{bad,define}}}), 934 wait_req_scan(St). 935 936scan_define_2(Arity, Def, {_,_,Key}=Mac, From, #epp{macs=Ms}=St) -> 937 case Ms of 938 #{Key:=Defs} when is_list(Defs) -> 939 %% User defined macros: can be overloaded 940 case proplists:is_defined(Arity, Defs) of 941 true -> 942 epp_reply(From, {error,{loc(Mac),epp,{redefine,Key}}}), 943 wait_req_scan(St); 944 false -> 945 scan_define_cont(From, St, Key, Defs, Arity, Def) 946 end; 947 #{Key:=_} -> 948 %% Predefined macros: cannot be overloaded 949 epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,Key}}}), 950 wait_req_scan(St); 951 _ -> 952 scan_define_cont(From, St, Key, [], Arity, Def) 953 end. 954 955%%% Detection of circular macro expansions (which would either keep 956%%% the compiler looping forever, or run out of memory): 957%%% When a macro is defined, we store the names of other macros it 958%%% uses in St#epp.uses. If any macro is undef'ed, that information 959%%% becomes invalid, so we redo it for all remaining macros. 960%%% The circularity detection itself is done when a macro is expanded: 961%%% the information from St#epp.uses is traversed, and if a circularity 962%%% is detected, an error message is thrown. 963 964scan_define_cont(F, #epp{macs=Ms0}=St, M, Defs, Arity, Def) -> 965 Ms = Ms0#{M=>[{Arity,Def}|Defs]}, 966 try macro_uses(Def) of 967 U -> 968 Uses0 = St#epp.uses, 969 Val = [{Arity,U}|case Uses0 of 970 #{M:=UseList} -> UseList; 971 _ -> [] 972 end], 973 Uses = Uses0#{M=>Val}, 974 scan_toks(F, St#epp{uses=Uses,macs=Ms}) 975 catch 976 {error, Location, Reason} -> 977 epp_reply(F, {error,{Location,epp,Reason}}), 978 wait_req_scan(St) 979 end. 980 981macro_uses({_Args, Tokens}) -> 982 Uses0 = macro_ref(Tokens), 983 lists:usort(Uses0). 984 985macro_ref([]) -> 986 []; 987macro_ref([{'?', _}, {'?', _} | Rest]) -> 988 macro_ref(Rest); 989macro_ref([{'?', _}, {atom, _, A}=Atom | Rest]) -> 990 Lm = loc(Atom), 991 Arity = count_args(Rest, Lm, A), 992 [{A,Arity} | macro_ref(Rest)]; 993macro_ref([{'?', _}, {var, _, A}=Var | Rest]) -> 994 Lm = loc(Var), 995 Arity = count_args(Rest, Lm, A), 996 [{A,Arity} | macro_ref(Rest)]; 997macro_ref([_Token | Rest]) -> 998 macro_ref(Rest). 999 1000%% scan_undef(Tokens, UndefToken, From, EppState) 1001 1002scan_undef([{'(',_Alp},{atom,_Am,M},{')',_Arp},{dot,_Ad}], _Undef, From, St) -> 1003 Macs = maps:remove(M, St#epp.macs), 1004 Uses = maps:remove(M, St#epp.uses), 1005 scan_toks(From, St#epp{macs=Macs, uses=Uses}); 1006scan_undef([{'(',_Alp},{var,_Am,M},{')',_Arp},{dot,_Ad}], _Undef, From,St) -> 1007 Macs = maps:remove(M, St#epp.macs), 1008 Uses = maps:remove(M, St#epp.uses), 1009 scan_toks(From, St#epp{macs=Macs, uses=Uses}); 1010scan_undef(Toks, Undef, From, St) -> 1011 T = find_mismatch(['(',var_or_atom,')',dot], Toks, Undef), 1012 epp_reply(From, {error,{loc(T),epp,{bad,undef}}}), 1013 wait_req_scan(St). 1014 1015%% scan_include(Tokens, IncludeToken, From, St) 1016 1017scan_include(Tokens0, Inc, From, St) -> 1018 Tokens = coalesce_strings(Tokens0), 1019 scan_include1(Tokens, Inc, From, St). 1020 1021scan_include1([{'(',_Alp},{string,_Af,NewName0}=StringT,{')',_Arp},{dot,_Ad}], 1022 _Inc, From, St) -> 1023 NewName = expand_var(NewName0), 1024 enter_file(NewName, StringT, From, St); 1025scan_include1(Toks, Inc, From, St) -> 1026 T = find_mismatch(['(',string,')',dot], Toks, Inc), 1027 epp_reply(From, {error,{loc(T),epp,{bad,include}}}), 1028 wait_req_scan(St). 1029 1030%% scan_include_lib(Tokens, IncludeToken, From, EppState) 1031%% For include_lib we first test if we can find the file through the 1032%% normal search path, if not we assume that the first directory name 1033%% is a library name, find its true directory and try with that. 1034 1035expand_lib_dir(Name) -> 1036 try 1037 [App|Path] = filename:split(Name), 1038 LibDir = code:lib_dir(list_to_atom(App)), 1039 {ok,fname_join([LibDir|Path])} 1040 catch 1041 _:_ -> 1042 error 1043 end. 1044 1045scan_include_lib(Tokens0, Inc, From, St) -> 1046 Tokens = coalesce_strings(Tokens0), 1047 scan_include_lib1(Tokens, Inc, From, St). 1048 1049scan_include_lib1([{'(',_Alp},{string,_Af,_NewName0},{')',_Arp},{dot,_Ad}], 1050 Inc, From, St) 1051 when length(St#epp.sstk) >= 8 -> 1052 epp_reply(From, {error,{loc(Inc),epp,{depth,"include_lib"}}}), 1053 wait_req_scan(St); 1054scan_include_lib1([{'(',_Alp},{string,_Af,NewName0}=N,{')',_Arp},{dot,_Ad}], 1055 _Inc, From, St) -> 1056 NewName = expand_var(NewName0), 1057 Loc = start_loc(St#epp.location), 1058 case file:path_open(St#epp.path, NewName, [read]) of 1059 {ok,NewF,Pname} -> 1060 wait_req_scan(enter_file2(NewF, Pname, From, St, Loc)); 1061 {error,_E1} -> 1062 case expand_lib_dir(NewName) of 1063 {ok,Header} -> 1064 case file:open(Header, [read]) of 1065 {ok,NewF} -> 1066 wait_req_scan(enter_file2(NewF, Header, From, 1067 St, Loc)); 1068 {error,_E2} -> 1069 epp_reply(From, 1070 {error,{loc(N),epp, 1071 {include,lib,NewName}}}), 1072 wait_req_scan(St) 1073 end; 1074 error -> 1075 epp_reply(From, {error,{loc(N),epp, 1076 {include,lib,NewName}}}), 1077 wait_req_scan(St) 1078 end 1079 end; 1080scan_include_lib1(Toks, Inc, From, St) -> 1081 T = find_mismatch(['(',string,')',dot], Toks, Inc), 1082 epp_reply(From, {error,{loc(T),epp,{bad,include_lib}}}), 1083 wait_req_scan(St). 1084 1085%% scan_ifdef(Tokens, IfdefToken, From, EppState) 1086%% scan_ifndef(Tokens, IfdefToken, From, EppSate) 1087%% Handle the conditional parsing of a file. 1088%% Report a badly formed if[n]def test and then treat as undefined macro. 1089 1090scan_ifdef([{'(',_Alp},{atom,_Am,M},{')',_Arp},{dot,_Ad}], _IfD, From, St) -> 1091 case is_macro_defined(M, St) of 1092 true -> 1093 scan_toks(From, St#epp{istk=[ifdef|St#epp.istk]}); 1094 false -> 1095 skip_toks(From, St, [ifdef]) 1096 end; 1097scan_ifdef([{'(',_Alp},{var,_Am,M},{')',_Arp},{dot,_Ad}], _IfD, From, St) -> 1098 case is_macro_defined(M, St) of 1099 true -> 1100 scan_toks(From, St#epp{istk=[ifdef|St#epp.istk]}); 1101 false -> 1102 skip_toks(From, St, [ifdef]) 1103 end; 1104scan_ifdef(Toks, IfDef, From, St) -> 1105 T = find_mismatch(['(',var_or_atom,')',dot], Toks, IfDef), 1106 epp_reply(From, {error,{loc(T),epp,{bad,ifdef}}}), 1107 wait_req_skip(St, [ifdef]). 1108 1109scan_ifndef([{'(',_Alp},{atom,_Am,M},{')',_Arp},{dot,_Ad}], _IfnD, From, St) -> 1110 case is_macro_defined(M, St) of 1111 true -> 1112 skip_toks(From, St, [ifndef]); 1113 false -> 1114 scan_toks(From, St#epp{istk=[ifndef|St#epp.istk]}) 1115 end; 1116scan_ifndef([{'(',_Alp},{var,_Am,M},{')',_Arp},{dot,_Ad}], _IfnD, From, St) -> 1117 case is_macro_defined(M, St) of 1118 true -> 1119 skip_toks(From, St, [ifndef]); 1120 false -> 1121 scan_toks(From, St#epp{istk=[ifndef|St#epp.istk]}) 1122 end; 1123scan_ifndef(Toks, IfnDef, From, St) -> 1124 T = find_mismatch(['(',var_or_atom,')',dot], Toks, IfnDef), 1125 epp_reply(From, {error,{loc(T),epp,{bad,ifndef}}}), 1126 wait_req_skip(St, [ifndef]). 1127 1128is_macro_defined(Name, #epp{macs=Macs}) -> 1129 case Macs of 1130 #{Name := undefined} -> false; 1131 #{Name := _Def} -> true; 1132 #{} -> false 1133 end. 1134 1135%% scan_else(Tokens, ElseToken, From, EppState) 1136%% If we are in an if body then convert to else and skip, if we are in an 1137%% else or not in anything report an error. 1138 1139scan_else([{dot,_Ad}], Else, From, St) -> 1140 case St#epp.istk of 1141 ['else'|Cis] -> 1142 epp_reply(From, {error,{loc(Else), 1143 epp,{illegal,"repeated",'else'}}}), 1144 wait_req_skip(St#epp{istk=Cis}, ['else']); 1145 [_I|Cis] -> 1146 skip_toks(From, St#epp{istk=Cis}, ['else']); 1147 [] -> 1148 epp_reply(From, {error,{loc(Else),epp, 1149 {illegal,"unbalanced",'else'}}}), 1150 wait_req_scan(St) 1151 end; 1152scan_else(Toks, Else, From, St) -> 1153 T = no_match(Toks, Else), 1154 epp_reply(From, {error,{loc(T),epp,{bad,'else'}}}), 1155 wait_req_scan(St). 1156 1157%% scan_if(Tokens, IfToken, From, EppState) 1158%% Handle the conditional parsing of a file. 1159 1160scan_if([{'(',_}|_]=Toks, If, From, St) -> 1161 try eval_if(Toks, St) of 1162 true -> 1163 scan_toks(From, St#epp{istk=['if'|St#epp.istk]}); 1164 _ -> 1165 skip_toks(From, St, ['if']) 1166 catch 1167 throw:Error0 -> 1168 Error = case Error0 of 1169 {_,erl_parse,_} -> 1170 {error,Error0}; 1171 {error,ErrL,What} -> 1172 {error,{ErrL,epp,What}}; 1173 _ -> 1174 {error,{loc(If),epp,Error0}} 1175 end, 1176 epp_reply(From, Error), 1177 wait_req_skip(St, ['if']) 1178 end; 1179scan_if(Toks, If, From, St) -> 1180 T = no_match(Toks, If), 1181 epp_reply(From, {error,{loc(T),epp,{bad,'if'}}}), 1182 wait_req_skip(St, ['if']). 1183 1184eval_if(Toks0, St) -> 1185 Toks = expand_macros(Toks0, St), 1186 Es1 = case erl_parse:parse_exprs(Toks) of 1187 {ok,Es0} -> Es0; 1188 {error,E} -> throw(E) 1189 end, 1190 Es = rewrite_expr(Es1, St), 1191 assert_guard_expr(Es), 1192 Bs = erl_eval:new_bindings(), 1193 LocalFun = fun(_Name, _Args) -> 1194 error(badarg) 1195 end, 1196 try erl_eval:exprs(Es, Bs, {value,LocalFun}) of 1197 {value,Res,_} -> 1198 Res 1199 catch 1200 _:_ -> 1201 false 1202 end. 1203 1204assert_guard_expr([E0]) -> 1205 E = rewrite_expr(E0, none), 1206 case erl_lint:is_guard_expr(E) of 1207 false -> 1208 throw({bad,'if'}); 1209 true -> 1210 ok 1211 end; 1212assert_guard_expr(_) -> 1213 throw({bad,'if'}). 1214 1215%% Dual-purpose rewriting function. When the second argument is 1216%% an #epp{} record, calls to defined(Symbol) will be evaluated. 1217%% When the second argument is 'none', legal calls to our built-in 1218%% functions are eliminated in order to turn the expression into 1219%% a legal guard expression. 1220 1221rewrite_expr({call,_,{atom,_,defined},[N0]}, #epp{macs=Macs}) -> 1222 %% Evaluate defined(Symbol). 1223 N = case N0 of 1224 {var,_,N1} -> N1; 1225 {atom,_,N1} -> N1; 1226 _ -> throw({bad,'if'}) 1227 end, 1228 {atom,erl_anno:new(0),maps:is_key(N, Macs)}; 1229rewrite_expr({call,_,{atom,_,Name},As0}, none) -> 1230 As = rewrite_expr(As0, none), 1231 Arity = length(As), 1232 case erl_internal:bif(Name, Arity) andalso 1233 not erl_internal:guard_bif(Name, Arity) of 1234 false -> 1235 %% A guard BIF, an -if built-in, or an unknown function. 1236 %% Eliminate the call so that erl_lint will not complain. 1237 %% The call might fail later at evaluation time. 1238 to_conses(As); 1239 true -> 1240 %% An auto-imported BIF (not guard BIF). Not allowed. 1241 throw({bad,'if'}) 1242 end; 1243rewrite_expr([H|T], St) -> 1244 [rewrite_expr(H, St)|rewrite_expr(T, St)]; 1245rewrite_expr(Tuple, St) when is_tuple(Tuple) -> 1246 list_to_tuple(rewrite_expr(tuple_to_list(Tuple), St)); 1247rewrite_expr(Other, _) -> 1248 Other. 1249 1250to_conses([H|T]) -> 1251 {cons,erl_anno:new(0),H,to_conses(T)}; 1252to_conses([]) -> 1253 {nil,erl_anno:new(0)}. 1254 1255%% scan_elif(Tokens, EndifToken, From, EppState) 1256%% Handle the conditional parsing of a file. 1257%% Report a badly formed if test and then treat as false macro. 1258 1259scan_elif(_Toks, Elif, From, St) -> 1260 case St#epp.istk of 1261 ['else'|Cis] -> 1262 epp_reply(From, {error,{loc(Elif), 1263 epp,{illegal,"unbalanced",'elif'}}}), 1264 wait_req_skip(St#epp{istk=Cis}, ['else']); 1265 [_I|Cis] -> 1266 skip_toks(From, St#epp{istk=Cis}, ['elif']); 1267 [] -> 1268 epp_reply(From, {error,{loc(Elif),epp, 1269 {illegal,"unbalanced",elif}}}), 1270 wait_req_scan(St) 1271 end. 1272 1273%% scan_endif(Tokens, EndifToken, From, EppState) 1274%% If we are in an if body then exit it, else report an error. 1275 1276scan_endif([{dot,_Ad}], Endif, From, St) -> 1277 case St#epp.istk of 1278 [_I|Cis] -> 1279 scan_toks(From, St#epp{istk=Cis}); 1280 [] -> 1281 epp_reply(From, {error,{loc(Endif),epp, 1282 {illegal,"unbalanced",endif}}}), 1283 wait_req_scan(St) 1284 end; 1285scan_endif(Toks, Endif, From, St) -> 1286 T = no_match(Toks, Endif), 1287 epp_reply(From, {error,{loc(T),epp,{bad,endif}}}), 1288 wait_req_scan(St). 1289 1290%% scan_file(Tokens, FileToken, From, EppState) 1291%% Set the current file and line to the given file and line. 1292%% Note that the line of the attribute itself is kept. 1293 1294scan_file(Tokens0, Tf, From, St) -> 1295 Tokens = coalesce_strings(Tokens0), 1296 scan_file1(Tokens, Tf, From, St). 1297 1298scan_file1([{'(',_Alp},{string,_As,Name},{',',_Ac},{integer,_Ai,Ln},{')',_Arp}, 1299 {dot,_Ad}], Tf, From, St) -> 1300 Anno = erl_anno:new(Ln), 1301 enter_file_reply(From, Name, Anno, loc(Tf), generated), 1302 Ms0 = St#epp.macs, 1303 Ms = Ms0#{'FILE':={none,[{string,line1(),Name}]}}, 1304 Locf = loc(Tf), 1305 NewLoc = new_location(Ln, St#epp.location, Locf), 1306 Delta = get_line(element(2, Tf))-Ln + St#epp.delta, 1307 wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms}); 1308scan_file1(Toks, Tf, From, St) -> 1309 T = find_mismatch(['(', string, ',', integer, ')', dot], Toks, Tf), 1310 epp_reply(From, {error,{loc(T),epp,{bad,file}}}), 1311 wait_req_scan(St). 1312 1313new_location(Ln, Le, Lf) when is_integer(Lf) -> 1314 Ln+(Le-Lf); 1315new_location(Ln, {Le,_}, {Lf,_}) -> 1316 {Ln+(Le-Lf),1}. 1317 1318%% skip_toks(From, EppState, SkipIstack) 1319%% Skip over forms until current conditional has been exited. Handle 1320%% nested conditionals and repeated 'else's. 1321 1322skip_toks(From, St, [I|Sis]) -> 1323 case io:scan_erl_form(St#epp.file, '', St#epp.location) of 1324 {ok,[{'-',_Ah},{atom,_Ai,ifdef}|_Toks],Cl} -> 1325 skip_toks(From, St#epp{location=Cl}, [ifdef,I|Sis]); 1326 {ok,[{'-',_Ah},{atom,_Ai,ifndef}|_Toks],Cl} -> 1327 skip_toks(From, St#epp{location=Cl}, [ifndef,I|Sis]); 1328 {ok,[{'-',_Ah},{'if',_Ai}|_Toks],Cl} -> 1329 skip_toks(From, St#epp{location=Cl}, ['if',I|Sis]); 1330 {ok,[{'-',_Ah},{atom,_Ae,'else'}=Else|_Toks],Cl}-> 1331 skip_else(Else, From, St#epp{location=Cl}, [I|Sis]); 1332 {ok,[{'-',_Ah},{atom,_Ae,'elif'}=Elif|Toks],Cl}-> 1333 skip_elif(Toks, Elif, From, St#epp{location=Cl}, [I|Sis]); 1334 {ok,[{'-',_Ah},{atom,_Ae,endif}|_Toks],Cl} -> 1335 skip_toks(From, St#epp{location=Cl}, Sis); 1336 {ok,_Toks,Cl} -> 1337 skip_toks(From, St#epp{location=Cl}, [I|Sis]); 1338 {error,E,Cl} -> 1339 case E of 1340 {_,file_io_server,invalid_unicode} -> 1341 %% The compiler needs to know that there was 1342 %% invalid unicode characters in the file 1343 %% (and there is no point in continuing anyway 1344 %% since io server process has terminated). 1345 epp_reply(From, {error,E}), 1346 leave_file(wait_request(St), St); 1347 _ -> 1348 %% Some other invalid token, such as a bad floating 1349 %% point number. Just ignore it. 1350 skip_toks(From, St#epp{location=Cl}, [I|Sis]) 1351 end; 1352 {eof,Cl} -> 1353 leave_file(From, St#epp{location=Cl,istk=[I|Sis]}); 1354 {error,_E} -> 1355 epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}), 1356 leave_file(wait_request(St), St) %This serious, just exit! 1357 end; 1358skip_toks(From, St, []) -> 1359 scan_toks(From, St). 1360 1361skip_else(Else, From, St, ['else'|Sis]) -> 1362 epp_reply(From, {error,{loc(Else),epp,{illegal,"repeated",'else'}}}), 1363 wait_req_skip(St, ['else'|Sis]); 1364skip_else(_Else, From, St, ['elif'|Sis]) -> 1365 skip_toks(From, St, ['else'|Sis]); 1366skip_else(_Else, From, St, [_I]) -> 1367 scan_toks(From, St#epp{istk=['else'|St#epp.istk]}); 1368skip_else(_Else, From, St, Sis) -> 1369 skip_toks(From, St, Sis). 1370 1371skip_elif(_Toks, Elif, From, St, ['else'|_]=Sis) -> 1372 epp_reply(From, {error,{loc(Elif),epp,elif_after_else}}), 1373 wait_req_skip(St, Sis); 1374skip_elif(Toks, Elif, From, St, [_I]) -> 1375 scan_if(Toks, Elif, From, St); 1376skip_elif(_Toks, _Elif, From, St, Sis) -> 1377 skip_toks(From, St, Sis). 1378 1379%% macro_pars(Tokens, ArgStack, Token) 1380%% macro_expansion(Tokens, Token) 1381%% Extract the macro parameters and the expansion from a macro definition. 1382 1383macro_pars([{')',_Ap}=Par|Ex], Args, _T0) -> 1384 {ok, {lists:reverse(Args), macro_pars_end(Ex, Par)}}; 1385macro_pars([{var,_,Name}=T|Ex], Args, _T0) -> 1386 check_macro_arg(Name, Args, T), 1387 macro_pars_cont(Ex, [Name|Args], T); 1388macro_pars(Toks, _Args, T0) -> 1389 T = no_match(Toks, T0), 1390 throw({error,loc(T),{bad,define}}). 1391 1392macro_pars_cont([{')',_Ap}=Par|Ex], Args, _T0) -> 1393 {ok, {lists:reverse(Args), macro_pars_end(Ex, Par)}}; 1394macro_pars_cont([{',',_Ad},{var,_,Name}=T|Ex], Args, _T0) -> 1395 check_macro_arg(Name, Args, T), 1396 macro_pars_cont(Ex, [Name|Args], T); 1397macro_pars_cont(Toks, _Args, T0) -> 1398 T = no_match(Toks, T0), 1399 throw({error,loc(T),{bad,define}}). 1400 1401macro_pars_end([{',',_Ad}=Comma|Ex], _T0) -> 1402 macro_expansion(Ex, Comma); 1403macro_pars_end(Toks, T0) -> 1404 T = no_match(Toks, T0), 1405 throw({error,loc(T),missing_comma}). 1406 1407macro_expansion([{')',_Ap},{dot,_Ad}], _T0) -> []; 1408macro_expansion([{dot,_}=Dot], _T0) -> 1409 throw({error,loc(Dot),missing_parenthesis}); 1410macro_expansion([T|Ts], _T0) -> 1411 [T|macro_expansion(Ts, T)]; 1412macro_expansion([], T0) -> throw({error,loc(T0),premature_end}). 1413 1414check_macro_arg(Name, Args, T) -> 1415 case lists:member(Name, Args) of 1416 true -> 1417 throw({error,loc(T),{duplicated_argument,Name}}); 1418 false -> 1419 ok 1420 end. 1421 1422%% expand_macros(Tokens, St) 1423%% Expand the macros in a list of tokens, making sure that an expansion 1424%% gets the same location as the macro call. 1425 1426expand_macros(MacT, M, Toks, St) -> 1427 #epp{macs=Ms,uses=U} = St, 1428 Lm = loc(MacT), 1429 Anno = element(2, MacT), 1430 case expand_macro1(Lm, M, Toks, Ms) of 1431 {ok,{none,Exp}} -> 1432 check_uses([{M,none}], [], U, Lm), 1433 Toks1 = expand_macros(expand_macro(Exp, Anno, [], #{}), St), 1434 expand_macros(Toks1++Toks, St); 1435 {ok,{As,Exp}} -> 1436 check_uses([{M,length(As)}], [], U, Lm), 1437 {Bs,Toks1} = bind_args(Toks, Lm, M, As, #{}), 1438 expand_macros(expand_macro(Exp, Anno, Toks1, Bs), St) 1439 end. 1440 1441expand_macro1(Lm, M, Toks, Ms) -> 1442 Arity = count_args(Toks, Lm, M), 1443 case Ms of 1444 #{M:=undefined} -> 1445 %% Predefined macro without definition. 1446 throw({error,Lm,{undefined,M,Arity}}); 1447 #{M:=[{none,Def}]} -> 1448 {ok,Def}; 1449 #{M:=Defs} when is_list(Defs) -> 1450 case proplists:get_value(Arity, Defs) of 1451 undefined -> 1452 throw({error,Lm,{mismatch,M}}); 1453 Def -> 1454 {ok,Def} 1455 end; 1456 #{M:=PreDef} -> 1457 %% Predefined macro. 1458 {ok,PreDef}; 1459 _ -> 1460 %% Macro not found. 1461 throw({error,Lm,{undefined,M,Arity}}) 1462 end. 1463 1464check_uses([], _Anc, _U, _Lm) -> 1465 ok; 1466check_uses([M|Rest], Anc, U, Lm) -> 1467 case lists:member(M, Anc) of 1468 true -> 1469 {Name,Arity} = M, 1470 throw({error,Lm,{circular,Name,Arity}}); 1471 false -> 1472 L = get_macro_uses(M, U), 1473 check_uses(L, [M|Anc], U, Lm), 1474 check_uses(Rest, Anc, U, Lm) 1475 end. 1476 1477get_macro_uses({M,Arity}, U) -> 1478 case U of 1479 #{M:=L} -> 1480 proplists:get_value(Arity, L, proplists:get_value(none, L, [])); 1481 _ -> 1482 [] 1483 end. 1484 1485%% Macro expansion 1486%% Note: io:scan_erl_form() does not return comments or white spaces. 1487expand_macros([{'?',_Aq},{atom,_Am,M}=MacT|Toks], St) -> 1488 expand_macros(MacT, M, Toks, St); 1489%% Special macros 1490expand_macros([{'?',_Aq},{var,Lm,'FUNCTION_NAME'}=Token|Toks], St0) -> 1491 St = update_fun_name(Token, St0), 1492 case St#epp.fname of 1493 undefined -> 1494 [{'?',_Aq},Token]; 1495 {Name,_} -> 1496 [{atom,Lm,Name}] 1497 end ++ expand_macros(Toks, St); 1498expand_macros([{'?',_Aq},{var,Lm,'FUNCTION_ARITY'}=Token|Toks], St0) -> 1499 St = update_fun_name(Token, St0), 1500 case St#epp.fname of 1501 undefined -> 1502 [{'?',_Aq},Token]; 1503 {_,Arity} -> 1504 [{integer,Lm,Arity}] 1505 end ++ expand_macros(Toks, St); 1506expand_macros([{'?',_Aq},{var,Lm,'LINE'}=Tok|Toks], St) -> 1507 Line = erl_scan:line(Tok), 1508 [{integer,Lm,Line}|expand_macros(Toks, St)]; 1509expand_macros([{'?',_Aq},{var,_Am,M}=MacT|Toks], St) -> 1510 expand_macros(MacT, M, Toks, St); 1511%% Illegal macros 1512expand_macros([{'?',_Aq},Token|_Toks], _St) -> 1513 T = case erl_scan:text(Token) of 1514 Text when is_list(Text) -> 1515 Text; 1516 undefined -> 1517 Symbol = erl_scan:symbol(Token), 1518 io_lib:fwrite(<<"~tp">>, [Symbol]) 1519 end, 1520 throw({error,loc(Token),{call,[$?|T]}}); 1521expand_macros([T|Ts], St) -> 1522 [T|expand_macros(Ts, St)]; 1523expand_macros([], _St) -> []. 1524 1525%% bind_args(Tokens, MacroLocation, MacroName, ArgumentVars, Bindings) 1526%% Collect the arguments to a macro call. 1527 1528bind_args([{'(',_Alp},{')',_Arp}|Toks], _Lm, _M, [], Bs) -> 1529 {Bs,Toks}; 1530bind_args([{'(',_Alp}|Toks0], Lm, M, [A|As], Bs) -> 1531 {Arg,Toks1} = macro_arg(Toks0, [], []), 1532 macro_args(Toks1, Lm, M, As, store_arg(Lm, M, A, Arg, Bs)); 1533bind_args(_Toks, Lm, M, _As, _Bs) -> 1534 throw({error,Lm,{mismatch,M}}). % Cannot happen. 1535 1536macro_args([{')',_Arp}|Toks], _Lm, _M, [], Bs) -> 1537 {Bs,Toks}; 1538macro_args([{',',_Ac}|Toks0], Lm, M, [A|As], Bs) -> 1539 {Arg,Toks1} = macro_arg(Toks0, [], []), 1540 macro_args(Toks1, Lm, M, As, store_arg(Lm, M, A, Arg, Bs)); 1541macro_args([], Lm, M, _As, _Bs) -> 1542 throw({error,Lm,{arg_error,M}}); % Cannot happen. 1543macro_args(_Toks, Lm, M, _As, _Bs) -> 1544 throw({error,Lm,{mismatch,M}}). % Cannot happen. 1545 1546store_arg(L, M, _A, [], _Bs) -> 1547 throw({error,L,{mismatch,M}}); 1548store_arg(_L, _M, A, Arg, Bs) -> 1549 Bs#{A=>Arg}. 1550 1551%% count_args(Tokens, MacroLine, MacroName) 1552%% Count the number of arguments in a macro call. 1553count_args([{'(', _Alp},{')',_Arp}|_Toks], _Lm, _M) -> 1554 0; 1555count_args([{'(', _Alp},{',',_Ac}|_Toks], Lm, M) -> 1556 throw({error,Lm,{arg_error,M}}); 1557count_args([{'(',_Alp}|Toks0], Lm, M) -> 1558 {_Arg,Toks1} = macro_arg(Toks0, [], []), 1559 count_args(Toks1, Lm, M, 1); 1560count_args(_Toks, _Lm, _M) -> 1561 none. 1562 1563count_args([{')',_Arp}|_Toks], _Lm, _M, NbArgs) -> 1564 NbArgs; 1565count_args([{',',_Ac},{')',_Arp}|_Toks], Lm, M, _NbArgs) -> 1566 throw({error,Lm,{arg_error,M}}); 1567count_args([{',',_Ac}|Toks0], Lm, M, NbArgs) -> 1568 {_Arg,Toks1} = macro_arg(Toks0, [], []), 1569 count_args(Toks1, Lm, M, NbArgs+1); 1570count_args([], Lm, M, _NbArgs) -> 1571 throw({error,Lm,{arg_error,M}}); 1572count_args(_Toks, Lm, M, _NbArgs) -> 1573 throw({error,Lm,{mismatch,M}}). % Cannot happen. 1574 1575%% macro_arg([Tok], [ClosePar], [ArgTok]) -> {[ArgTok],[RestTok]}. 1576%% Collect argument tokens until we hit a ',' or a ')'. We know a 1577%% enough about syntax to recognise "open parentheses" and keep 1578%% scanning until matching "close parenthesis". 1579 1580macro_arg([{',',Lc}|Toks], [], Arg) -> 1581 {lists:reverse(Arg),[{',',Lc}|Toks]}; 1582macro_arg([{')',Lrp}|Toks], [], Arg) -> 1583 {lists:reverse(Arg),[{')',Lrp}|Toks]}; 1584macro_arg([{'(',Llp}|Toks], E, Arg) -> 1585 macro_arg(Toks, [')'|E], [{'(',Llp}|Arg]); 1586macro_arg([{'<<',Lls}|Toks], E, Arg) -> 1587 macro_arg(Toks, ['>>'|E], [{'<<',Lls}|Arg]); 1588macro_arg([{'[',Lls}|Toks], E, Arg) -> 1589 macro_arg(Toks, [']'|E], [{'[',Lls}|Arg]); 1590macro_arg([{'{',Llc}|Toks], E, Arg) -> 1591 macro_arg(Toks, ['}'|E], [{'{',Llc}|Arg]); 1592macro_arg([{'begin',Lb}|Toks], E, Arg) -> 1593 macro_arg(Toks, ['end'|E], [{'begin',Lb}|Arg]); 1594macro_arg([{'if',Li}|Toks], E, Arg) -> 1595 macro_arg(Toks, ['end'|E], [{'if',Li}|Arg]); 1596macro_arg([{'case',Lc}|Toks], E, Arg) -> 1597 macro_arg(Toks, ['end'|E], [{'case',Lc}|Arg]); 1598macro_arg([{'fun',Lc}|[{'(',_}|_]=Toks], E, Arg) -> 1599 macro_arg(Toks, ['end'|E], [{'fun',Lc}|Arg]); 1600macro_arg([{'fun',_}=Fun,{var,_,_}=Name|[{'(',_}|_]=Toks], E, Arg) -> 1601 macro_arg(Toks, ['end'|E], [Name,Fun|Arg]); 1602macro_arg([{'receive',Lr}|Toks], E, Arg) -> 1603 macro_arg(Toks, ['end'|E], [{'receive',Lr}|Arg]); 1604macro_arg([{'try',Lr}|Toks], E, Arg) -> 1605 macro_arg(Toks, ['end'|E], [{'try',Lr}|Arg]); 1606macro_arg([{'cond',Lr}|Toks], E, Arg) -> 1607 macro_arg(Toks, ['end'|E], [{'cond',Lr}|Arg]); 1608macro_arg([{Rb,Lrb}|Toks], [Rb|E], Arg) -> %Found matching close 1609 macro_arg(Toks, E, [{Rb,Lrb}|Arg]); 1610macro_arg([T|Toks], E, Arg) -> 1611 macro_arg(Toks, E, [T|Arg]); 1612macro_arg([], _E, Arg) -> 1613 {lists:reverse(Arg),[]}. 1614 1615%% expand_macro(MacroDef, MacroTokenAnno, RestTokens, Bindings) 1616%% expand_arg(Argtokens, MacroTokens, TokenAnno, RestTokens, Bindings) 1617%% Insert the macro expansion replacing macro parameters with their 1618%% argument values, inserting the location of first the macro call 1619%% and then the macro arguments, i.e. simulate textual expansion. 1620 1621expand_macro([{var,_Av,V}|Ts], Anno, Rest, Bs) -> 1622 case Bs of 1623 #{V:=Val} -> 1624 expand_arg(Val, Ts, Anno, Rest, Bs); 1625 _ -> 1626 [{var,Anno,V}|expand_macro(Ts, Anno, Rest, Bs)] 1627 end; 1628expand_macro([{'?', _}, {'?', _}, {var,_Av,V}|Ts], Anno, Rest, Bs) -> 1629 case Bs of 1630 #{V:=Val} -> 1631 expand_arg(stringify(Val, Anno), Ts, Anno, Rest, Bs); 1632 _ -> 1633 [{var,Anno,V}|expand_macro(Ts, Anno, Rest, Bs)] 1634 end; 1635expand_macro([T|Ts], Anno, Rest, Bs) -> 1636 [setelement(2, T, Anno)|expand_macro(Ts, Anno, Rest, Bs)]; 1637expand_macro([], _Anno, Rest, _Bs) -> Rest. 1638 1639expand_arg([A|As], Ts, _Anno, Rest, Bs) -> 1640 %% It is not obvious that the annotation of arguments should 1641 %% replace _Anno. 1642 NextAnno = element(2, A), 1643 [A|expand_arg(As, Ts, NextAnno, Rest, Bs)]; 1644expand_arg([], Ts, Anno, Rest, Bs) -> 1645 expand_macro(Ts, Anno, Rest, Bs). 1646 1647%%% 1648%%% Here follows support for the ?FUNCTION_NAME and ?FUNCTION_ARITY 1649%%% macros. Since the parser has not been run yet, we don't know the 1650%%% name and arity of the current function. Therefore, we will need to 1651%%% scan the beginning of the current form to extract the name and 1652%%% arity of the function. 1653%%% 1654 1655update_fun_name(Token, #epp{fname=Toks0}=St) when is_list(Toks0) -> 1656 %% ?FUNCTION_NAME or ?FUNCTION_ARITY is used for the first time in 1657 %% a function. First expand macros (except ?FUNCTION_NAME and 1658 %% ?FUNCTION_ARITY) in the form. 1659 1660 Toks1 = (catch expand_macros(Toks0, St#epp{fname=undefined})), 1661 1662 %% Now extract the name and arity from the stream of tokens, and store 1663 %% the result in the #epp{} record so we don't have to do it 1664 %% again. 1665 1666 case Toks1 of 1667 [{atom,_,Name},{'(',_}|Toks] -> 1668 %% This is the beginning of a function definition. 1669 %% Scan the token stream up to the matching right 1670 %% parenthesis and count the number of arguments. 1671 FA = update_fun_name_1(Toks, 1, {Name,0}, St), 1672 St#epp{fname=FA}; 1673 [{'?',_}|_] -> 1674 %% ?FUNCTION_NAME/?FUNCTION_ARITY used at the beginning 1675 %% of a form. Does not make sense. 1676 {var,_,Macro} = Token, 1677 throw({error,loc(Token),{illegal_function_usage,Macro}}); 1678 _ when is_list(Toks1) -> 1679 %% Not the beginning of a function (an attribute or a 1680 %% syntax error). 1681 {var,_,Macro} = Token, 1682 throw({error,loc(Token),{illegal_function,Macro}}); 1683 _ -> 1684 %% A macro expansion error. Return a dummy value and 1685 %% let the caller notice and handle the error. 1686 St#epp{fname={'_',0}} 1687 end; 1688update_fun_name(_Token, St) -> 1689 St. 1690 1691update_fun_name_1([Tok|Toks], L, FA, St) -> 1692 case classify_token(Tok) of 1693 comma -> 1694 if 1695 L =:= 1 -> 1696 {Name,Arity} = FA, 1697 update_fun_name_1(Toks, L, {Name,Arity+1}, St); 1698 true -> 1699 update_fun_name_1(Toks, L, FA, St) 1700 end; 1701 left -> 1702 update_fun_name_1(Toks, L+1, FA, St); 1703 right when L =:= 1 -> 1704 FA; 1705 right -> 1706 update_fun_name_1(Toks, L-1, FA, St); 1707 other -> 1708 case FA of 1709 {Name,0} -> 1710 update_fun_name_1(Toks, L, {Name,1}, St); 1711 {_,_} -> 1712 update_fun_name_1(Toks, L, FA, St) 1713 end 1714 end; 1715update_fun_name_1([], _, FA, _) -> 1716 %% Syntax error, but never mind. 1717 FA. 1718 1719classify_token({C,_}) -> classify_token_1(C); 1720classify_token(_) -> other. 1721 1722classify_token_1(',') -> comma; 1723classify_token_1('(') -> left; 1724classify_token_1('{') -> left; 1725classify_token_1('[') -> left; 1726classify_token_1('<<') -> left; 1727classify_token_1(')') -> right; 1728classify_token_1('}') -> right; 1729classify_token_1(']') -> right; 1730classify_token_1('>>') -> right; 1731classify_token_1(_) -> other. 1732 1733 1734%%% stringify(Ts, Anno) returns a list of one token: a string which when 1735%%% tokenized would yield the token list Ts. 1736 1737%% erl_scan:text(T) is not backward compatible with this. 1738%% Note that escaped characters will be replaced by themselves. 1739token_src({dot, _}) -> 1740 "."; 1741token_src({X, _}) when is_atom(X) -> 1742 atom_to_list(X); 1743token_src({var, _, X}) -> 1744 atom_to_list(X); 1745token_src({char,_,C}) -> 1746 io_lib:write_char(C); 1747token_src({string, _, X}) -> 1748 io_lib:write_string(X); 1749token_src({_, _, X}) -> 1750 io_lib:format("~w", [X]). 1751 1752stringify1([]) -> 1753 []; 1754stringify1([T | Tokens]) -> 1755 [io_lib:format(" ~ts", [token_src(T)]) | stringify1(Tokens)]. 1756 1757stringify(Ts, Anno) -> 1758 [$\s | S] = lists:flatten(stringify1(Ts)), 1759 [{string, Anno, S}]. 1760 1761coalesce_strings([{string,A,S} | Tokens]) -> 1762 coalesce_strings(Tokens, A, [S]); 1763coalesce_strings([T | Tokens]) -> 1764 [T | coalesce_strings(Tokens)]; 1765coalesce_strings([]) -> 1766 []. 1767 1768coalesce_strings([{string,_,S}|Tokens], A, S0) -> 1769 coalesce_strings(Tokens, A, [S | S0]); 1770coalesce_strings(Tokens, A, S) -> 1771 [{string,A,lists:append(lists:reverse(S))} | coalesce_strings(Tokens)]. 1772 1773find_mismatch([Tag|Tags], [{Tag,_A}=T|Ts], _T0) -> 1774 find_mismatch(Tags, Ts, T); 1775find_mismatch([Tag|Tags], [{Tag,_A,_V}=T|Ts], _T0) -> 1776 find_mismatch(Tags, Ts, T); 1777find_mismatch([var_or_atom|Tags], [{var,_A,_V}=T|Ts], _T0) -> 1778 find_mismatch(Tags, Ts, T); 1779find_mismatch([var_or_atom|Tags], [{atom,_A,_N}=T|Ts], _T0) -> 1780 find_mismatch(Tags, Ts, T); 1781find_mismatch(_, Ts, T0) -> 1782 no_match(Ts, T0). 1783 1784no_match([T|_], _T0) -> 1785 T; 1786no_match(_, T0) -> 1787 T0. 1788 1789%% epp_request(Epp) 1790%% epp_request(Epp, Request) 1791%% epp_reply(From, Reply) 1792%% Handle communication with the epp. 1793 1794epp_request(Epp) -> 1795 wait_epp_reply(Epp, erlang:monitor(process, Epp)). 1796 1797epp_request(Epp, Req) -> 1798 Epp ! {epp_request,self(),Req}, 1799 wait_epp_reply(Epp, erlang:monitor(process, Epp)). 1800 1801epp_reply(From, Rep) -> 1802 From ! {epp_reply,self(),Rep}, 1803 ok. 1804 1805wait_epp_reply(Epp, Mref) -> 1806 receive 1807 {epp_reply,Epp,Rep} -> 1808 erlang:demonitor(Mref, [flush]), 1809 Rep; 1810 {'DOWN',Mref,_,_,E} -> 1811 receive {epp_reply,Epp,Rep} -> Rep 1812 after 0 -> exit(E) 1813 end 1814 end. 1815 1816expand_var([$$ | _] = NewName) -> 1817 case catch expand_var1(NewName) of 1818 {ok, ExpName} -> 1819 ExpName; 1820 _ -> 1821 NewName 1822 end; 1823expand_var(NewName) -> 1824 NewName. 1825 1826expand_var1(NewName) -> 1827 [[$$ | Var] | Rest] = filename:split(NewName), 1828 Value = os:getenv(Var), 1829 true = Value =/= false, 1830 {ok, fname_join([Value | Rest])}. 1831 1832fname_join(["." | [_|_]=Rest]) -> 1833 fname_join(Rest); 1834fname_join(Components) -> 1835 filename:join(Components). 1836 1837loc(Token) -> 1838 erl_scan:location(Token). 1839 1840add_line(Line, Offset) when is_integer(Line) -> 1841 Line+Offset; 1842add_line({Line, Column}, Offset) -> 1843 {Line+Offset, Column}. 1844 1845start_loc(Line) when is_integer(Line) -> 1846 1; 1847start_loc({_Line, _Column}) -> 1848 {1, 1}. 1849 1850line1() -> 1851 erl_anno:new(1). 1852 1853get_line(Anno) -> 1854 erl_anno:line(Anno). 1855 1856%% epp has always output -file attributes when entering and leaving 1857%% included files (-include, -include_lib). Starting with R11B the 1858%% -file attribute is also recognized in the input file. This is 1859%% mainly aimed at yecc, the parser generator, which uses the -file 1860%% attribute to get correct lines in messages referring to code 1861%% supplied by the user (actions etc in .yrl files). 1862%% 1863%% In a perfect world (read: perfectly implemented applications such 1864%% as Xref, Cover, Debugger, etc.) it would not be necessary to 1865%% distinguish -file attributes from epp and the input file. The 1866%% Debugger for example could have one window for each referred file, 1867%% each window with its own set of breakpoints etc. The line numbers 1868%% of the abstract code would then point into different windows 1869%% depending on the -file attribute. [Note that if, as is the case for 1870%% yecc, code has been copied into the file, then it is possible that 1871%% the copied code differs from the one referred to by the -file 1872%% attribute, which means that line numbers can mismatch.] In practice 1873%% however it is very rare with Erlang functions in included files, so 1874%% only one window is used per module. This means that the line 1875%% numbers of the abstract code have to be adjusted to refer to the 1876%% top-most source file. The function interpret_file_attributes/1 1877%% below interprets the -file attribute and returns forms where line 1878%% numbers refer to the top-most file. The -file attribute forms that 1879%% have been output by epp (corresponding to -include and 1880%% -include_lib) are kept, but the user's -file attributes are 1881%% removed. This seems sufficient for now. 1882%% 1883%% It turns out to be difficult to distinguish -file attributes in the 1884%% input file from the ones added by epp unless some action is taken. 1885%% The solution employed is to let epp label the annotation of user 1886%% supplied -file attributes as 'generated'. 1887 1888interpret_file_attribute(Forms) -> 1889 interpret_file_attr(Forms, 0, []). 1890 1891interpret_file_attr([{attribute,Anno,file,{File,Line}}=Form | Forms], 1892 Delta, Fs) -> 1893 L = get_line(Anno), 1894 Generated = erl_anno:generated(Anno), 1895 if 1896 Generated -> 1897 %% -file attribute 1898 interpret_file_attr(Forms, (L + Delta) - Line, Fs); 1899 not Generated -> 1900 %% -include or -include_lib 1901 % true = L =:= Line, 1902 case Fs of 1903 [_, File | Fs1] -> % end of included file 1904 [Form | interpret_file_attr(Forms, 0, [File | Fs1])]; 1905 _ -> % start of included file 1906 [Form | interpret_file_attr(Forms, 0, [File | Fs])] 1907 end 1908 end; 1909interpret_file_attr([Form0 | Forms], Delta, Fs) -> 1910 F = fun(Anno) -> 1911 Line = erl_anno:line(Anno), 1912 erl_anno:set_line(Line + Delta, Anno) 1913 end, 1914 Form = erl_parse:map_anno(F, Form0), 1915 [Form | interpret_file_attr(Forms, Delta, Fs)]; 1916interpret_file_attr([], _Delta, _Fs) -> 1917 []. 1918