# module Crystal::Macros

##
Overview

The `Macros` module is a fictitious module used to document macros and macro methods.

You can invoke a fixed subset of methods on AST nodes at compile-time. These methods are documented on the classes in this module. Additionally, methods of the `Macros` module are top-level methods that you can invoke, like `#puts` and `#run`.

##
Defined in:

compiler/crystal/macros.cr

##
Instance Method Summary

- #`(command) : MacroId

  Executes a system command and returns the output as a `MacroId`.

- #compare_versions(v1 : StringLiteral, v2 : StringLiteral) : NumberLiteral

  Compares two semantic versions.

- #debug(format = true) : Nop

  Outputs the current macro's buffer to the standard output.

- #env(name) : StringLiteral | NilLiteral

  Gets the value of an environment variable at compile-time, or `nil` if it doesn't exist.

- #file_exists?(filename) : BoolLiteral

  Returns `true` if the given filename exists, `false` otherwise.

- #flag?(name) : BoolLiteral

  Returns whether a compile-time flag is set.

- #host_flag?(name) : BoolLiteral

  Returns whether a compile-time flag is set for the host platform, which can differ from the target platform (`#flag?`) during cross-compilation.

- #p(*expressions) : Nop

  Same as `#puts`.

- #p!(*expressions) : Nop

  Prints macro expressions together with their values at compile-time.

- #parse_type(type_name : StringLiteral) : Path | Generic | ProcNotation | Metaclass

  Parses type_name into a `Path`, `Generic` (also used for unions), `ProcNotation`, or `Metaclass`.

- #pp(*expressions) : Nop

  Same as `#puts`.

- #pp!(*expressions) : Nop

  Same as `#p!`

- #print(*expressions) : Nop

  Prints AST nodes at compile-time.

- #puts(*expressions) : Nop

  Prints AST nodes at compile-time.

- #raise(message) : NoReturn

  Gives a compile-time error with the given message.

- #read_file(filename) : StringLiteral

  Reads a file and returns a `StringLiteral` with its contents.

- #read_file?(filename) : StringLiteral | NilLiteral

  Same as `#read_file`, except that `nil` is returned on any I/O failure instead of issuing a compile-time failure.

- #run(filename, *args) : MacroId

  Compiles and execute a Crystal program and returns its output as a `MacroId`.

- #skip_file : Nop

  Skips the rest of the file from which it is executed.

- #system(command) : MacroId

  Executes a system command and returns the output as a `MacroId`.

- #warning(message : StringLiteral) : NilLiteral

  Emits a compile-time warning with the given message.

##
Instance Method Detail

def `(command) : MacroId #

Executes a system command and returns the output as a `MacroId`. Gives a compile-time error if the command failed to execute.

It is impossible to call this method with any regular call syntax. There is an associated literal type which calls the method with the literal content as command:

    {{ `echo hi` }} # => "hi\n"

See `Command` literals in the language reference.

def compare_versions(v1 : StringLiteral, v2 : StringLiteral) : NumberLiteral #

Compares two semantic versions.

Returns `-1`, `0` or `1` depending on whether v1 is lower than v2, equal to v2 or greater than v2.

    {{ compare_versions("1.10.0", "1.2.0") }} # => 1

def debug(format = true) : Nop #

Outputs the current macro's buffer to the standard output. Useful for debugging a macro to see what's being generated.

By default, the output is tried to be formatted using Crystal's formatter, but you can disable this by passing `false` to this method.

def env(name) : StringLiteral | NilLiteral #

Gets the value of an environment variable at compile-time, or `nil` if it doesn't exist.

def file_exists?(filename) : BoolLiteral #

Returns `true` if the given filename exists, `false` otherwise.

def flag?(name) : BoolLiteral #

Returns whether a compile-time flag is set.

    {{ flag?(:x86_64) }} # true or false

def host_flag?(name) : BoolLiteral #

Returns whether a compile-time flag is set for the host platform, which can differ from the target platform (`#flag?`) during cross-compilation.

    {{ host_flag?(:win32) }} # true or false

def p(*expressions) : Nop #

Same as `#puts`.

def p!(*expressions) : Nop #

Prints macro expressions together with their values at compile-time. Useful for debugging macros.

def parse_type(type_name : StringLiteral) : Path | Generic | ProcNotation | Metaclass #

Parses type_name into a `Path`, `Generic` (also used for unions), `ProcNotation`, or `Metaclass`.

The `#resolve` method could then be used to resolve the value into a `TypeNode`, if the type_name represents a type, otherwise the value of the constant.

A compile time error is raised if the type/constant does not actually exist, or if a required generic argument was not provided.

    class Foo; end

    struct Some::Namespace::Foo; end

    module Bar(T); end

    MY_CONST = 1234

    {{ parse_type("Foo").resolve.class? }}                                   # => true
    {{ parse_type("Some::Namespace::Foo").resolve.struct? }}                 # => true
    {{ parse_type("Foo|Some::Namespace::Foo").resolve.union_types.size }}    # => 2
    {{ parse_type("Bar(Int32)|Foo").resolve.union_types[0].type_vars.size }} # => 1
    {{ parse_type("MY_CONST").resolve }}                                     # => 1234

    {{ parse_type("MissingType").resolve }}   # Error: undefined constant MissingType
    {{ parse_type("UNKNOWN_CONST").resolve }} # Error: undefined constant UNKNOWN_CONST

def pp(*expressions) : Nop #

Same as `#puts`.

def pp!(*expressions) : Nop #

Same as `#p!`

def print(*expressions) : Nop #

Prints AST nodes at compile-time. Useful for debugging macros.

def puts(*expressions) : Nop #

Prints AST nodes at compile-time. Useful for debugging macros.

def raise(message) : NoReturn #

Gives a compile-time error with the given message.

def read_file(filename) : StringLiteral #

Reads a file and returns a `StringLiteral` with its contents.

Gives a compile-time error if the file doesn't exist or if reading the file fails.

To read a file relative to where the macro is defined, use:

    read_file("#{__DIR__}/some_file.txt")

NOTE Relative paths are resolved to the current working directory.

def read_file?(filename) : StringLiteral | NilLiteral #

Same as `#read_file`, except that `nil` is returned on any I/O failure instead of issuing a compile-time failure.

def run(filename, *args) : MacroId #

Compiles and execute a Crystal program and returns its output as a `MacroId`.

The file denoted by filename must be a valid Crystal program. This macro invocation passes args to the program as regular program arguments. This output is the result of this macro invocation, as a `MacroId`.

The `#run` macro is useful when the subset of available macro methods are not enough for your purposes and you need something more powerful. With `#run` you can read files at compile time, connect to the internet or to a database.

A simple example:

    # read.cr
    puts File.read(ARGV[0])

    # main.cr
    macro read_file_at_compile_time(filename)
      {{ run("./read", filename).stringify }}
    end

    puts read_file_at_compile_time("some_file.txt")

The above generates a program that will have the contents of `some_file.txt`. The file, however, is read at compile time and will not be needed at runtime.

NOTE the compiler is allowed to cache the executable generated for filename and only recompile it if any of the files it depends on changes (their modified time). This is why it's strongly discouraged to use a program for `#run` that changes in subsequent compilations (for example, if it executes shell commands at compile time, or other macro run programs). It's also strongly discouraged to have a macro run program take a lot of time, because this will slow down compilation times. Reading files is OK, opening an HTTP connection at compile-time will most likely result in very slow compilations.

def skip_file : Nop #

Skips the rest of the file from which it is executed. Typical usage is to skip files that have platform specific code, without having to surround the most relevant code in `{% if flag?(...) %} ... {% end %}` macro blocks.

Example:

    # sth_for_osx.cr
    {% skip_file unless flag?(:darwin) %}

    # Class FooForMac will only be defined if we're compiling on OS X
    class FooForMac
    end

def system(command) : MacroId #

Executes a system command and returns the output as a `MacroId`. Gives a compile-time error if the command failed to execute.

    {{ system("echo hi") }} # => "hi\n"

def warning(message : StringLiteral) : NilLiteral #

Emits a compile-time warning with the given message.