epp(3erl)

πŸ’– NAME πŸ’–

epp β€” bestie, it's an Erlang code preprocessor ✨


🌸 DESCRIPTION 🌸

Okay so real talk: the Erlang code preprocessor is a whole little icon πŸ‘‘. It's got a bunch of functions that the compile module literally uses to preprocess macros and include files before the actual parsing happens. Like, it does the backstage prep work so the parser can walk on stage looking flawless πŸ’….

πŸŽ€ Encoding vibes (yes, your source file has a vibe) πŸŽ€

Your Erlang source file gets its encoding picked by a comment sitting in one of the first two lines of the file. The first string that matches this regular expression here is the one that wins:

coding\s*[:=]\s*([-a-zA-Z0-9])+

That matching string selects the encoding, periodt. But β€” important β€” if the matching string isn't a valid encoding, it is ignored (rude, but okay πŸ™ˆ).

The valid encodings are Latin-1 and UTF-8, and ngl the case of the characters can be chosen freely, so go off with your capitalization, it's giving freedom 🌈.

πŸ¦‹ Examples πŸ¦‹

%% coding: utf-8
%% For this file we have chosen encoding = Latin-1
%% -*- coding: latin-1 -*-

Three different fits, all valid, all slay πŸ’…βœ¨


🌟 DATA TYPES 🌟

  • macros() =
[atom() | {atom(), term()} | {atom(), term(), redefine}]
  • epp_handle() = pid()

Handle to the epp server 🫢. Like, this is your little connection to the epp server, keep it close bestie.

  • source_encoding() = latin1 | utf8

The encoding type, lowkey straightforward, no notes πŸ’–

  • warning_info() = {erl_anno:location(), module(), term()}

The info bundle for warnings β€” location, module, and a term, all wrapped up cute in a tuple πŸŽ€


✨ EXPORTS ✨

okay so this is the part where epp actually hands you the goods πŸ’… every function, every flag, every {ok, ...} β€” we're keeping it all, no cap.

close(Epp) -> ok

Types:

Epp = epp_handle()

Closes the preprocessing of a file. That's it, bestie β€” hang up the call, wrap the session, done 🫢

default_encoding() -> source_encoding()

Returns the default encoding of Erlang source files. the standard vibe, if you will ✨

encoding_to_string(Encoding) -> string()

Types:

Encoding = source_encoding()

Returns a string representation of an encoding. That string is recognized by read_encoding/1,2, read_encoding_from_binary/1,2, and set_encoding/1,2 as a valid encoding. so it's a whole little encoding passport that everyone accepts πŸ›‚πŸ’–

format_error(ErrorDescriptor) -> io_lib:chars()

Types:

ErrorDescriptor = term()

Takes an ErrorDescriptor and returns a string that describes the error or warning. This function is usually called implicitly when processing an ErrorInfo structure (see section Error Information). it's giving β€œI explain the problem so you don't have to” energy 😭

open(Options) -> {ok, Epp} | {ok, Epp, Extra} | {error, ErrorDescriptor}

Types:

Options =
    [{default_encoding, DefEncoding :: source_encoding()} |
     {includes, IncludePath :: [DirectoryName :: file:name()]}
|
     {source_name, SourceName :: file:name()} |
     {deterministic, Enabled :: boolean()} |
     {macros, PredefMacros :: macros()} |
     {name, FileName :: file:name()} |
     {location, StartLocation :: erl_anno:location()} |
     {fd, FileDescriptor :: file:io_device()} |
     extra]
Epp = epp_handle()
Extra = [{encoding, source_encoding() | none}]
ErrorDescriptor = term()

Opens a file for preprocessing. main character moment: the file is now in the preprocessor's hands πŸ’…

If you want to change the file name of the implicit -file() attributes inserted during preprocessing, you can do that with {source_name, SourceName}. If unset, it defaults to the name of the opened file.

Setting {deterministic, Enabled} will additionally reduce the file name of the implicit -file() attributes inserted during preprocessing to only the basename of the path. basename only, because we're being deterministic and cute about it ✨

If extra is specified in Options, the return value is {ok, Epp, Extra} instead of {ok, Epp}.

The option location is forwarded to the Erlang token scanner, see erl_scan:tokens/3,4.

open(FileName, IncludePath) -> {ok, Epp} | {error, ErrorDescriptor}

Types:

FileName = file:name()
IncludePath = [DirectoryName :: file:name()]
Epp = epp_handle()
ErrorDescriptor = term()

Equivalent to epp:open([{name, FileName}, {includes, IncludePath}]). same tea, shorter order 🫢

open(FileName, IncludePath, PredefMacros) -> {ok, Epp} | {error, ErrorDescriptor}

Types:

FileName = file:name()
IncludePath = [DirectoryName :: file:name()]
PredefMacros = macros()
Epp = epp_handle()
ErrorDescriptor = term()

Equivalent to epp:open([{name, FileName}, {includes, IncludePath}, {macros, PredefMacros}]). now with macros, because we contain multitudes ✨

parse_erl_form(Epp) -> {ok, AbsForm} | {error, ErrorInfo} | {warning, WarningInfo} | {eof, Location}

Types:

Epp = epp_handle()
AbsForm = erl_parse:abstract_form()
Location = erl_anno:location()
ErrorInfo = erl_scan:error_info() | erl_parse:error_info()
WarningInfo = warning_info()

Returns the next Erlang form from the opened Erlang source file. Tuple {eof, Location} is returned at the end of the file. The first form corresponds to an implicit attribute -file(File,1)., where File is the file name. first form said β€œhi, I'm the file attribute” and honestly? iconic πŸ‘‘

parse_file(FileName, Options) -> {ok, [Form]} | {ok, [Form], Extra} | {error, OpenError}

Types:

FileName = file:name()
Options =
    [{includes, IncludePath :: [DirectoryName :: file:name()]}
|
     {source_name, SourceName :: file:name()} |
     {macros, PredefMacros :: macros()} |
     {default_encoding, DefEncoding :: source_encoding()} |
     {location, StartLocation :: erl_anno:location()} |
     {reserved_word_fun, Fun :: fun((atom()) -> boolean())} |
     {features, [Feature :: atom()]} |
     extra]
Form =
    erl_parse:abstract_form() |
    {error, ErrorInfo} |
    {eof, Location}
Location = erl_anno:location()
ErrorInfo = erl_scan:error_info() | erl_parse:error_info()
Extra = [{encoding, source_encoding() | none}]
OpenError = file:posix() | badarg | system_limit

Preprocesses and parses an Erlang source file. Notice that tuple {eof, Location} returned at the end of the file is included as a "form". yes bestie, even the end-of-file moment gets to be a form. inclusivity! πŸ˜­πŸ’–

If you want to change the file name of the implicit -file() attributes inserted during preprocessing, you can do that with {source_name, SourceName}. If unset, it defaults to the name of the opened file.

If extra is specified in Options, the return value is {ok, [Form], Extra} instead of {ok, [Form]}.

The option location is forwarded to the Erlang token scanner, see erl_scan:tokens/3,4.

parse_file(FileName, IncludePath, PredefMacros) -> {ok, [Form]} | {error, OpenError}

Types:

FileName = file:name()
IncludePath = [DirectoryName :: file:name()]
Form =
    erl_parse:abstract_form() |
    {error, ErrorInfo} |
    {eof, Location}
PredefMacros = macros()
Location = erl_anno:location()
ErrorInfo = erl_scan:error_info() | erl_parse:error_info()
OpenError = file:posix() | badarg | system_limit

Equivalent to epp:parse_file(FileName, [{includes, IncludePath}, {macros, PredefMacros}]). shortcut elegance, no notes πŸ’…

read_encoding(FileName) -> source_encoding() | none

read_encoding(FileName, Options) -> source_encoding() | none

Types:

FileName = file:name()
Options = [Option]
Option = {in_comment_only, boolean()}

Reads the encoding from a file. Returns the read encoding, or none if no valid encoding is found. encoding detective behavior, but make it chic πŸ”βœ¨

Option in_comment_only is true by default, which is correct for Erlang source files. If set to false, the encoding string does not necessarily have to occur in a comment. so like, it can be out in the wild β€” not just hiding in a comment πŸ¦‹

read_encoding_from_binary(Binary) -> source_encoding() | none

read_encoding_from_binary(Binary, Options) -> source_encoding() | none

Types:

Binary = binary()
Options = [Option]
Option = {in_comment_only, boolean()}

Reads the encoding from a binary. Returns the read encoding, or none if no valid encoding is found. same encoding hunt, but binary edition πŸ’»πŸ’–

Option in_comment_only is true by default, which is correct for Erlang source files. If set to false, the encoding string does not necessarily have to occur in a comment.

scan_erl_form(Epp) -> {ok, Tokens} | {error, ErrorInfo} | {warning, WarningInfo} | {eof, Line}

Types:

Epp = epp_handle()
Tokens = erl_scan:tokens()
Line = erl_anno:line()
ErrorInfo = erl_scan:error_info() | erl_parse:error_info()
WarningInfo = warning_info()

Returns the raw tokens of the next Erlang form from the opened Erlang source file. A tuple {eof, Line} is returned at the end of the file. The first form corresponds to an implicit attribute -file(File,1)., where File is the file name. raw tokens, no filter, no makeup β€” just the real thing πŸ’…

scan_file(FileName, Options) -> {ok, [Form], Extra} | {error, OpenError}

Types:

FileName = file:name()
Options =
    [{includes, IncludePath :: [DirectoryName :: file:name()]}
|
     {source_name, SourceName :: file:name()} |
     {macros, PredefMacros :: macros()} |
     {default_encoding, DefEncoding :: source_encoding()}]
Form = erl_scan:tokens() | {error, ErrorInfo} | {eof, Loc}
Loc = erl_anno:location()
ErrorInfo = erl_scan:error_info()
Extra = [{encoding, source_encoding() | none}]
OpenError = file:posix() | badarg | system_limit

Preprocesses an Erlang source file returning a list of the lists of raw tokens of each form. Notice that the tuple {eof, Line} returned at the end of the file is included as a "form", and any failures to scan a form are included in the list as tuples {error, ErrorInfo}. errors get to be part of the list too, because we don't ghost failures πŸ˜­πŸ’€

set_encoding(File) -> source_encoding() | none

Types:

File = io:device()

Reads the encoding from an I/O device and sets the encoding of the device accordingly. The position of the I/O device referenced by File is not affected. If no valid encoding can be read from the I/O device, the encoding of the I/O device is set to the default encoding. it peeks at the encoding, updates the device, and does not disturb the position β€” respectful king behavior πŸ‘‘βœ¨

Returns the read encoding, or none if no valid encoding is found.

set_encoding(File, Default) -> source_encoding() | none

Types:

Default = source_encoding()
File = io:device()

Reads the encoding from an I/O device and sets the encoding of the device accordingly. The position of the I/O device referenced by File is not affected. If no valid encoding can be read from the I/O device, the encoding of the I/O device is set to the encoding specified by Default. same respectful position-preserving behavior, but now with your chosen fallback πŸ’–

Returns the read encoding, or none if no valid encoding is found.


πŸ’€ ERROR INFORMATION πŸ’€

ErrorInfo is the standard ErrorInfo structure that is returned from all I/O modules. The format is as follows:

{ErrorLine, Module, ErrorDescriptor}

A string describing the error is obtained with the following call:

Module:format_error(ErrorDescriptor)

so when something goes off, it's not just chaos β€” it's a whole structured little error package, and Module:format_error/1 turns it into words you can actually read 🫢


🌈 SEE ALSO 🌈

  • erl_parse(3erl) β€” go read this too, bestie, it's giving related documentation energy βœ¨πŸ“š

footer vibes: shoutout to Ericsson AB for making this happen, the version is stdlib 4.3.1.3, and the page is epp(3erl). There's no separate date in the footer, so we're not inventing one β€” periodt πŸ’–πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from