-
Notifications
You must be signed in to change notification settings - Fork 2
Macro definition
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.
-
Name (
post-condition-loop
, right aftermacro
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".
"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.)
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)
.
Original
for i = 0, i < 10, i++
Console.WriteLine(i)
Result
i = 0
while i < 10
Console.WriteLine(i)
i++
Expressions
Public compiler interface
-
Units & Modules
- TODO -
Compiler
class
Backend
- Lexer (Tokenizer)
- Syntax tree
- Traversing