-
Notifications
You must be signed in to change notification settings - Fork 0
Macro assembler syntax
Macros is an usual mechanism of many different assembler implementations. It uses to produce any parameter-driven pieces of assembler code and insert them into your assembler input stream. The macros, implemented in this assembler, are oriented on maximum macro generation performance. To support it, all the macros you define in your input stream are compiled on-the-fly to Java classes and executed by the JVM directly. To support this ability, all the macro parameters and variables in this implementation of the macro language are strongly typed.
Syntax of the macro language is line-oriented. Every macro operator occupies own separate line in the input stream. A set of macro operators is:
- .macro - macro description (the same first operator in the macro)
- .local - local macro variable definition
- .set - macro assignment operator
- .if/.elseif/.else/.end - conditionals
- .while/.end and .for/.end - loops
- .choise/.of/.otherwise/.end - choise from alternatives
- .error - throwing macro exception
- .break and .continue - controlling loop
- .exit - exit execution macros
- [.mend] - the same last operator in the macro. Terminates macro definition
Any other sequences in the lines are treated as text lines and will be simply passed to the output. A special construction &name in the lines is treated as macro substitution and replaces with the current value of the 'name' variable during transmission. Nested (recursive) substitutions are not supported. Name can be either macro parameter or macro local variable name. To separate variable name from the text continuation, a dot(.) can be used:
before&var1&var2.inside&var2&var1.after
If 'var1' value is 100 and 'var2' value is 'test', result of this substitution will be:
before100testinsidetest100after
Data types supported in the macro assembler are:
- int - is mapping to Java long
- real - is mapping to Java double
- str - is mapping to Java char[]
- bool - is mapping to Java boolean
Constants of the types are:
- 123 - int type constant
- 35.3 and 2.54e-20 - real type constant
- "abb[p;lkcvl;kdsvdsf-0dvoi\r\n" - str type constant (Java escapes inside double quotest are supported)
- true and false - bool type constant
This operator is the same first in the macro definition. Every macros can have a set of positional and key parameters. Positional parameters are defined by it's position in the macro parameter list, key parameters are defined by it's unique names in the macro parameters list. All the positional parameters must precede all key parameters in the .macro, positional and key parameters mix is illegal. Syntax of the .macro is:
<.macro> ::= <name> .macro [<positional>[,...]]<key>=[<default>][,...]
<name> ::= <any_Java_compatible_identifier>
<positional> ::= <name>:<type>
<key> ::= <name>:<type>
<default> ::= <constantExpression>
Example of the operator is:
sum .macro left:int,right:int,checkOverflow:bool=false
catlist .macro source:str,target:str,concat:str,prefix:str=,suffix:str=
This command defines a new macros sum with two positional parameters (with value type int) and one key parameter (with value type bool) and default value false. Positional parameters may not have default values, key parameters can. Macro name must be unique in the given input stream. Parameter names must be unique in the given macro. Default value or the key parameter need be either constant or constant expression to calculate it on the compilation stage.
This operator defines new local variable in the macros. Syntax of the .local is:
<.local> ::= <name> .local <type>[=<initialValue>] <initialValue> ::= <constantExpression>
.local operator must immediately follows either .macro or another .local operator only. Local variable name need be unique in the given macro. Initial value need be either constant or constant expression to calculate it on the compilation stage.
Example of the operator is:
myVariable .local int
myInitial .local str="initial string"
These commands define local variable myVariable (with value type int) with no any initial values and local variable myInitial (with value type *str) with initial value 'initial string'.
This is an assignment operator in the macros. Syntax of the .set is:
<.set> ::= <name> .set <expression>
Expression can be any syntactically correct expression. It can contains constants, positional and key parameters, local variables, built-in functions, brackets () and operators.
Complete operator list, ordered by it's priority, is:
| operator | result | conv | comment |
|---|---|---|---|
| -N | N | Y | negation |
| N + N, N - N | N | Y | additions |
| N * N, N / N, N % N | N | Y | multiplications |
| X # X | S | Y | concatenation |
| B ? X : X | X | Y | ternary operation |
| X < X, X <= X, X > X, X >= X, X == X, X != X | B | comparison | |
| !B | B | logical NOT | |
| B && B | B | logical AND | |
| B || B | B | logical OR |
Letter means:
- X - any type
- I - int type
- R - real type
- N - any numeric type (int and/or real)
- S - str type
- B - bool type
- Y - automatic data conversion is supported for the given operators
- y - partially automatic data conversion is supported for the given operators
Complete built-in function list is:
| function | result type | comment |
|---|---|---|
| exists(X) | B | test existent of any value for the variable and parameters only. Any other expression types always return true |
| int(X) | I | convert expression to integer |
| real(X) | R | convert expression to real |
| str(X) | S | convert expression to string |
| bool(X) | B | convert expression to bool |
| uniqueG() | I | get unique number inside the given assembler input stream |
| uniqueL() | I | get unique number inside the given execution of the macro |
bool type can be converted to the str type only.
Example of the operator is:
myVar .set myVar # "123" # (x > 0) ? Y % 20 : 0 # (2 + Z) / 3.5
Any constant expression will be calculated on the compilation stage. All the variables in the expression must be initialized, otherwise exception will be thrown. Use exists(X) function to check initialization of the variable. Use data conversion function when automatic data conversion is not supported.
This operator is conditional operator in the macros. Syntax of the .if is:
<.if> ::= .if <expression1>
. . .
[.elseif <expression2>]
. . .
[.elseif <expressionN>]
. . .
[.else]
. . .
.end
Expression type must be bool only. As .elseif, so .else can be missing. .if operator can be nested.
Example of the operator is:
.if x==0
lconst_0
.elseif x==1
lconst_1
.else
ldc2_w &x
.end
This operator is the pre-conditional loop operator in the macros. Syntax of the .while is:
<.while> ::= [<label>:] .while <expressison> . . . .end
<label> ::= <name>
Expression type must be bool only. .while operator can be nested. Optional label need be unique in the given macro. It can be used in the .break and .continue operators to break/continue outside the nested loops.
Example of the operator is:
x .set 10
.while x > 0
label&x: nop
x .set x - 1
.end
This operator is the loop-with-the-variable operator. Syntax of the .for is:
<.for> ::= [<label>:] .for <var> = <initial> to <terminal> [step <step>]
. . .
.end
<initial> ::= <expression>
<terminal> ::= <expression>
<step> ::= <expression>
All expressions need be numeric. Missing step clause defaults 1. Loop variable must be declared by the .local operator. .for operator can be nested. Optional label need be unique in the given macro. It can be used in the .break and .continue operators to break/continue outside the nested loops.
Example of the operator is:
.for x = 1 to 10
label&x: nop
.end
This operator is analog of the Pascal language case/of operator. Syntax of the .choise is:
<.choise> ::= .choise <expression>
.of <expression1>
. . .
.of <expression2>
. . .
.of <expressionN>
. . .
[.otherwise]
. . .
.end
Expressions can be any sorts (not only constants), but value types of both .choise and .of expressions must be identical. .otherwise clause can be missing. All the .of clauses are mutually exclusive. .choise operator can be nested.
Example of the operator is:
.choise x
.of y
&x=&x
.otherwise
&y=&x
.end
This operator throws chav1961.purelib.basic.exceptions.Calculation exception during macro execution. Syntax of the .error is:
<.error> ::= .error <expression>
Expression can be any string expression. It is passed as parameter to Calculation exception constructor.
Example of the operator is:
.error "'X' parameter in not typed on the macro call"
This operator is analog of the return statement in most of languages. Syntax of the .exit is:
<.exit> ::= .exit
These operators are analogs of the break and continue statements in Java. Syntax of them is:
<.break> ::= .break [<label>]
<.continue> ::= .continue [<label>]
Optional label can be any label of the nesting .for/.while operators.
To execute macro inside your assembler code, simply type it's name and parameters required:
zzz: sum 2,3 // Macros with posiitonals and missing key
sum -100,22,checkOverflow=true // Macros with positionals and explicitly defined key
Macro call is not strongly required to quote str parameters. You must do it only when your parameter value contains commas (,) or equal signs (=):
catlist abc, def, "=3" // 1-st and 2-nd parameters can be used without quotes
catlist "abc", "def", "=3" // ... but can be used with the quotes also
catlist a b c, d e f, "=3" // every parameters is bounded with (,)
catlist "a b c", "d e f", "=3" // ... or explicitly with the quotes
catlist "a,b,c", "d e f", "=3" //
Any positional parameters can be skipped on the macro call:
catlist ,def,"=3" // the same first parameter
catlist abc,,"=3" // the second parameter
catlist abc,def // the same last parameter
Positional parameters skipped will remain uninitialized (exists(...) will return false on it).
Key parameters need follow after all the positional one. It's order is not significant and defines by it's names only:
calculate 100,200,mul=true,add=true
calculate 100,200,add=true,mul=true
Key parameters skipped will either remain uninitalized when have no default value (exists(...) will return false on it) or receive default value when have it.
All the macros in the input stream are compiled to Java byte code and stored into the internal repository of the AsmWriter instance. After closing AsmWriter, all the compiled macros inside will be removed. To reuse these macros, you can call special method AsmWriter.clone(OutputStream) to create new AsmWriter instance with the currently defined macros repository. You can also add a set of new macros into the cloned AsmWriter instance. They will be locally defined macros only and they will be removed from everywhere after closing cloned AsmWriter instance. Calling AsmWriter.clone(OutputStream) allow you to build any AsmWriter hierarchy in your program
(c) 2016-2018 Alexander Chernomyrdin aka chav1961, Pure Library Wiki