Skip to content

Macro definition

Nikitin Ilya edited this page Apr 29, 2020 · 11 revisions

Macro is the preferred way of adding new syntax to language.

Let's start by example:

macro post-condition-loop ('do', scope: Scope, ('while' | 'until'), condition: Infix)
    if syntax[2] == 'while'
        condition = {{ not $condition }}
    
    return {{
        while true {
            $scope
            if $condition {
                break
            }
        }
    }}

This is a definition for commonly known do-while (& do-until) statement.

Main parts of macro

  • Name (post-condition-loop, right after macro keyword).
    This name can be used to directly import it from some module.

  • Syntax definition (goes right after name, the ('do', ..., condition: Infix) part).
    For a detailed explanation, please refer to Macro syntax definition page.

  • Macro expansion logic (indented block right after name and syntax).
    Describes how macro should be processed and converted to regular code during compilation.

As you can see, first comes an if statement. It compares, if macro reference found in code uses while keyword, and not until one.
syntax is a special collection-variable that maps each matched macro syntax part with 0-based index.
(e. g., if you write do { print(x) } until y, syntax will contain these elements:

0 - keyword   - 'do'
1 - block     - { print(x) }
2 - keyword   - 'until'
3 - condition - y

Also, each syntax element can be accessed by it's name, if it is specified (block, condition).
As you can see below, condition is reassigned to a new value, {{ not $condition }}.

This double-braced expression is known as "code quote".

Code quotes & "unquotes"

"Quote" means that this piece of code is a regular object (just like new Car(), etc in other languages).
This is a main idea of "code as data" concept (also known as "homoiconicity") — you can treat code piece as an object, and directly change it's properties.

Here, we replaced the old value of condition with a new one — unary expression with not operator.
(Internally, compiler represents it as new UnaryExpr(TokenType.OpNot, condition), this is better explained in Compiler architecture section).

$ operator (named unquoted code) is a reverse for {{}} — it dereferences code object to it's value. You must use it in code quote expressions if you reference some object defined outside the quote.

So, the piece condition = {{ not $condition }} means — take the condition expression reference, and replace it with boolean-negative expression.

Last part of macro is a return statement. Macro should always return a code quote — it will be "injected" in place of macro usage.
(Please note, that code quotes always use braces in scopes, because indentation syntax is not supported inside any brackets, you can read more in Scopes syntax page.)

Macro vs Function

Some people may ask "What's the difference between 'function' and 'macro'?"

That's the main differences:

Macro is a "syntax transformation", and it's expansion is performed at compile time. Macro has no specific invocation syntax - when you write some custom syntax in your code, it can match to appropriate macro.

Function is a set of instructions that can be reused multiple times. It's evaluation is performed at runtime. Function usually has one consistent invocation syntax: func_name(arg1, arg2, etc).

To complete the explanation, look at the example result of macro expansion process

Original

for i = 0, i < 10, i++
	Console.WriteLine(i)

Result

i = 0
while i < 10
    Console.WriteLine(i)
    i++

Introduction to language syntax

Expressions

Toolset architecture

Public compiler interface

  • Units & Modules - TODO
  • Compiler class

Backend

  • Lexer (Tokenizer)
  • Syntax tree
  • Traversing

Miscellaneous

Clone this wiki locally